function EnumWindowsProc(const AWnd: HWND; const AParam: LPARAM): Boolean; stdcall; var Wnd: HWND; begin Wnd := HWND(AParam); if AWnd = Wnd then Result := True else Result := False; end; procedure TForm1.Button1Click(Sender: TObject); var Wnd: HWND; begin Wnd := Handle; EnumWindows(@EnumWindowsProc, LPARAM(Wnd)); end;
Ответ будет (автоматически) опубликован через две недели.
P.S. _Rouse, специально для тебя ;)
Возможно дело в типа, возвращаемом EnumWindowsProc? Boolean 1-байтный, в то время как BOOL 4-байтный. Не испортит ли это стек?
ОтветитьУдалитьИ еще есть разница в регистрах, куда ложится результат EnumWindowsProc. Для BOOL это будет eax, а для Boolean - другой. Как вариант - EnumWindowsProc вычитает значение не из того регистра
ОтветитьУдалитьну да. Если для BOOL будет eax , то для Boolean - это al. Илья, это часть одного регистра.
ОтветитьУдалитьПроблема в том, что старшие биты не будут инициализированы. А вызывающая подпрограмма проверяет результат на равенство нулю, то есть: равно нулю или не равно.
Наглядный листинг:
mov eax, 0FFFFFFFFh
mov al, 0 ; Result := false, инициализируется 1 байт при типе Boolean
; значение в регистре будет таким eax = 0FFFFFF00h
; а теперь проверочка
cmp eax, 0 ; if Result = 0 then
В подпрограмме EnumWindowsProc что будет в регистре eax, в момент присваивания результата, неизвестно. И в 99,99% случаях вызывающая подпрограмма получит ненулевое значение.
Этот комментарий был удален автором.
УдалитьХотя, нет, это уже явная подсказка.
УдалитьТогда просто упомяну, что сравнение идет через
test eax, eax
jz
Не суть, какой инструкцией результат будет проверен - логическим умножением или инструкцией сравнения. Внимание на самом значении.
УдалитьЯ успел увидеть подсказку)). Но аномалий с ней, с подсказкой, не заметил. Это касается win32. У меня еще не возникало задач, которые вынудили бы установить 64-битную систему. Выходит в ней и закавыка. Дождусь ответ от знатоков win64.
А в win32 этот код работает по той причине, что кодогенератор создает такой код: xor eax, eax для выражения Result := false.
Внутри же EnumWindows результат не задействуется, но возвращается в Button1Click через переменную fSuccess:
BOOL PseudoEnumWindows(..., WNDENUMPROC lpfn, ...)
{
UINT i;
UINT cHwnd;
HWND *phwndT;
HWND *phwndFirst;
BOOL fSuccess = TRUE;
if ((cHwnd = BuildHwndList(..., &phwndFirst) == -1) {
return FALSE;
}
phwndT = phwndFirst;
for (i = 0; i < cHwnd; i++) {
if (!(fSuccess = (*lpfn)(*phwndT, lParam)))
break;
phwndT++;
}
FinalizeHwndList;
return fSuccess;
}
P.S.: Теги "bold, italic и href" - хорошо. Но очень не хватает "pre и code".
>> Не суть, какой инструкцией результат будет проверен - логическим умножением или инструкцией сравнения.
УдалитьСмотря как стравнивать :)
test eax, eax
jz
и
cmp eax, 1
jnz
по сути идентичны, но дадут разное поведение на 32 битном регистре :)
Ой, пардон - что-то странное сказал.
УдалитьКонечно же:
test eax, eax
sbb eax, eax
inc eax
и
cmp eax, 1
sbb eax, eax
inc eax
в EAX результат
ЗЫ: это с учетом разницы между BOOL и Boolean (особенно второй вариант подвержен ошибке)
УдалитьЯ понял, что Вы имели в виду. Вы правы. Постами выше я руководствовался, исходя из справки, что результат может принимать два значения "нуль" и "не нуль". Отсюда происходит проверка на нулевое значение: cmp eax, 0 и test eax, eax , обе установят флаг нуля. Написав cmp eax, 0 я хотел подчеркнуть именно сравнение с нулем.
УдалитьИ эта заковыка даже на win32 демонстрирует это поведение - не обнуляется весь регистр, как это происходит в случае с явным идентификатором FALSE :
function EnumWindowsProc(const AWnd: HWND; const AParam: LPARAM): Boolean; stdcall;
begin
Result := not (AWnd = HWND(AParam));
end;
Абсолютно верно :)
УдалитьВы практически подошли к концу решения задачи, но... :)
Думаю, что скажу банальность - но разве не логично компилятору делать код, проверяющий на True и False простой проверкой младшего бита в младшем разряде регистра?
ОтветитьУдалитьВедь тогда не будет проблемы, когда результат то в eax, то в ax, то в al...
Вообще-то процессору вообще проще обнулять регистр целиком и сравнивать на равенство полного регистра с нулём, а не выделять из регистра байты и оперировать с ними.
УдалитьСобственно, при включенной оптимизации Delphi компилирует именно такой код, поэтому Boolean выше будет, фактически, скомпилирован в BOOL. При выключенной оптимизации компилятор Delphi компилирует буквальный код.
Проблема в том, что мы не контролируем вызывающий нас код (код Windows), а там может быть что угодно - как и сравнение младшего байта (навряд ли), так и сравнение полного регистра (скорее всего). Компилятору C вообще не нужно волноваться о "результат то в eax, то в ax, то в al".
Более того, не всегда даже выполняется "не-ноль - это истина" - иногда функции Windows требуют конкретного значения для True.