Листая в очередной раз MSDN решил попробовать передать CreateWindowEx в качестве параметра lpszClassName не адрес строки с именем класса, а атом, который возвращает RegisterClassEx. Вылетает с Exception'ом в модуле user32.dll по смещению 0x00011e87. Под отладчиком (Win32DAsm) - работает как часы: отображает окно, корректно реагирует на закрытие и т.п. Переписал, оставив только самое основное. Проблема осталась, всё абсолютно идентично. Исходник на FASM: Код (Text): include 'win32a.inc' format PE GUI 4.0 entry WinMain include 'equates\kernel32.inc' include 'equates\gdi32.inc' include 'equates\user32.inc' section '.code' code readable executable proc OnIdle ret endp proc WndProc hWnd, uMsg, wParam, lParam cmp [uMsg], WM_DESTROY je .WMDestroy jmp .Default .WMDestroy: invoke PostQuitMessage, ebx jmp .EndProc .Default: invoke DefWindowProc, [hWnd], [uMsg], [wParam], [lParam] .EndProc: ret endp proc WinMain xor ebx, ebx invoke GetModuleHandle, ebx mov [_hInstance], eax ; Filling WNDCLASSEXA mov [WC.cbSize], sizeof.WNDCLASSEXA mov [WC.style], CS_HREDRAW or CS_VREDRAW mov [WC.lpfnWndProc], WndProc mov [WC.cbClsExtra], ebx mov [WC.cbWndExtra], ebx mov eax, [_hInstance] mov [WC.hInstance], eax invoke LoadIcon, eax, IconName mov [WC.hIcon], eax mov [WC.hIconSm], eax invoke LoadCursor, ebx, IDC_ARROW mov [WC.hCursor], eax invoke GetSysColorBrush, COLOR_BTNFACE mov [WC.hbrBackground], eax mov [WC.lpszMenuName], ebx mov [WC.lpszClassName], ClassName invoke RegisterClassEx, addr WC test eax, eax jz .EndProc invoke CreateWindowEx, ebx, eax, WinName, WS_POPUP or WS_MAXIMIZE,\ ebx, ebx, ebx, ebx, ebx, ebx, [_hInstance], ebx test eax, eax jz .EndProc mov [hWindow], eax invoke ShowWindow, eax, SW_MAXIMIZE invoke UpdateWindow, [hWindow] .MsgLoop: invoke PeekMessage, Msg, ebx, ebx, ebx, PM_REMOVE test eax, eax jz .Idle invoke TranslateMessage, Msg invoke DispatchMessage, Msg .Idle: stdcall OnIdle jmp .MsgLoop .EndProc: invoke ExitProcess, ebx ret endp section '.data' data readable writeable IconName db "PROGRAM", 0 ClassName db "MYWNDCLS", 0 WinName db "MYWINDOW", 0 _hInstance dd ? WC WNDCLASSEXA hWindow dd ? Msg MSG section '.idata' import data readable writeable library kernel32, 'kernel32.dll',\ gdi32, 'gdi32.dll',\ user32, 'user32.dll' include 'apia\kernel32.inc' include 'apia\gdi32.inc' include 'apia\user32.inc' Ещё одно наблюдение. Старый-добрый трюк со вставкой пустого MessageBox'а показывает, что программа вываливается на CreateWindowEx(). Т.е. сразу после вызова этой функции MessageBox перестаёт выводиться, а перед ней - выводится. Правда, если перед CreateWindowEx(), то и Exception'а не возникает. Вставляемый вызов MessageBox() такой: Код (Text): pusha invoke MessageBox, ebx, ebx, ebx, 64 popa Значение, возвращаемое RegisterClassEx() при запуске из-под отладчика, равно 0x0000c34a, CreateWindowEx() там же даёт корректный хэндл (что неудивительно, иначе бы не работало и под отладчиком). Появилось подозрение, что всё-таки такой трюк не сработает (т.е. вызов CreateWindowEx(..., RegisterClassEx(...), ...)). Но хочется разобраться в сути происходящего. ОС WinXP SP2.
Virtual8086 Код (Text): invoke RegisterClassEx, addr WC test eax, eax jz .EndProc invoke CreateWindowEx, ebx, eax, WinName, WS_POPUP or WS_MAXIMIZE,\ ebx, ebx, ebx, ebx, ebx, ebx, [_hInstance], ebx После RegisterClassEx обнулите в eax hi.word (and eax,0000FFFFh), и тогда CreateWindowEx примет атом (lo.word) с удовольствием.
Хм, спасибо. Сработало. В описании функций видел упоминание про младшее слово, но W32DAsm показывал корректное значение. Больше отладчику доверять не буду
Код (Text): mov [WC.cbClsExtra], ebx mov [WC.cbWndExtra], ebx mov eax, [_hInstance] mov [WC.hInstance], eax invoke LoadIcon, eax, IconName mov [WC.hIcon], eax mov [WC.hIconSm], eax Объясните почему он двигает все время из регистра eax,ebx? откуда он знает что там те значения которые должны быть вот например mov [WC.cbClsExtra], ebx стало быть ригистр ebx==NULL
Это трюк, подсмотренный у демосценеров. В самом начале программы ebx ксорится с самим собой. Поскольку API-функции не меняют значения этого регистра, его в дальнейшем можно использовать везде, где надо подставить значение 0. Это сокращает на несколько байт каждую инструкцию, где immediate-значение 0 можно заменить регистром ebx. Касательно же eax... В первом случае в предшествующей строке значение eax считывается из заранее подготовленной ячейки [_hInstance], во втором - используется тот факт, что API-функции (и не только они %)) возвращают результат в eax.