(Почему) В Windows "Calc.exe" отсутствует WndProc?

Я возился с wndprocs и WinSpy ++, и я наткнулся на странную вещь с calc.exe.  Кажется, у него нет WndProc.

Вот мой скриншот: тестовая программа, которую я создал, окно WinSpy ++, показывающее N/A и виновника.

Возможно, инструмент немного устарел, но эмпирические данные не доказывают, что WndProc не существует.

Я не знаю, если это по дизайну (это было бы странно), или если я что-то упустил...

Вот ссылочный код:

Function FindWindow(title As String) As IntPtr
 Return AutoIt.AutoItX.WinGetHandle(title)
End Function
Function GetWindowProc(handle As IntPtr) As IntPtr
 Return GetWindowLong(handle, WindowLongFlags.GWL_WNDPROC)
End Function
2 ответа

Короче говоря (о вашем коде): GetWindowLong() терпит неудачу, потому что вы пытаетесь прочитать адрес в адресном пространстве целевого процесса.

ОБЪЯСНЕНИЕ

Когда GetWindowLong() возвращает 0, это означает, что из MSDN существует ошибка:

Если функция не работает, возвращаемое значение равно нулю. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.

Проверьте Marshal.GetLastWin32Error(), и вы, вероятно, увидите код ошибки ERROR_ACCESS_DENIED (числовое значение - 0x5).

Почему? Поскольку GetWindowLong() пытается получить адрес (или дескриптор) оконной процедуры (не в вашем коде, а в целевом процессе), теоретически это может быть даже оконная процедура по умолчанию, но я никогда не видел основного приложения окно, которое не мешает хотя бы нескольким сообщениям). Вы можете использовать этот трюк (но я никогда не пробовал!), Чтобы увидеть, использует ли окно по умолчанию (у вас есть адрес или нет), я не знаю... кто-то должен попробовать.

Теперь подумайте, что WNDPROC:

LRESULT (CALLBACK* WNDPROC) (HWND, UINT, WPARAM, LPARAM);

Адрес (действительный в процессе A) не может быть вызван в процессе B (где он вообще не имеет смысла). Сегменты кода Windows DLL разделяются между процессами (я предполагаю, что я не проверял, но это разумно в игре между безопасностью и производительностью).

Более того, CallWindowProc(NULL, ...) поймет, что NULL как специальное значение вызывает процедуру окна для этого класса окна (на HWND owner). Из MSDN:

... Если это значение получается путем вызова функции GetWindowLong... адреса окна или диалогового окна или специального внутреннего значения, значимого только для CallWindowProc.

Как это делает Microsoft Spy ++ (а, может быть, и WinSpy ++)? Трудно сказать без исходного кода WinSpy ++. Конечно, это не так легко, как GetWindowLong() и правильный путь должен включать CreateRemoteThread() и делать с ним LoadLibrary(), но исходный код Microsoft Spy ++ и WinSpy ++ недоступен (AFAIK) для дальнейшей проверки...

UPDATE

Проверка/отладка WinSpy ++ довольно непростая тема с вопросом (вы должны отправить билет разработчикам, ваш исходный код может завершиться неудачно, что я объяснил выше, вы должны - всегда - проверьте коды ошибок), но мы можем взглянуть на удовольствие.

В InjectThread.c мы видим, что он использует WriteProcessMemory + CreateRemoteThread, а затем ReadProcessMemory для чтения данных назад (не соответствующий код опущен):

// Write a copy of our injection thread into the remote process
WriteProcessMemory(hProcess, pdwRemoteCode, lpCode, cbCodeSize, &dwWritten);
// Write a copy of the INJTHREAD to the remote process. This structure
// MUST start on a 32bit boundary
pRemoteData = (void *)((BYTE *)pdwRemoteCode + ((cbCodeSize + 4) & ~ 3));
// Put DATA in the remote thread memory block
WriteProcessMemory(hProcess, pRemoteData, lpData, cbDataSize, &dwWritten);
hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, 
 (LPTHREAD_START_ROUTINE)pdwRemoteCode, pRemoteData, 0, &dwRemoteThreadId);
// Wait for the thread to terminate
WaitForSingleObject(hRemoteThread, INFINITE);
// Read the user-structure back again
if(!ReadProcessMemory(hProcess, pRemoteData, lpData, cbDataSize, &dwRead))
{
 //an error occurred
}

Процедура окна на вкладке "Общие" и на вкладке "Класс" отличается (на вкладке "Класс" она корректно отображает значение). Из DisplayClassInfo.c:

//window procedure
if(spy_WndProc == 0) 
{
 wsprintf(ach, _T("N/A"));
}
else 
{
 wsprintf(ach, szHexFmt, spy_WndProc);
 if(spy_WndProc != spy_WndClassEx.lpfnWndProc)
 lstrcat(ach, _T(" (Subclassed)"));
}
//class window procedure
if(spy_WndClassEx.lpfnWndProc == 0)
 wsprintf(ach, _T("N/A"));
else
 wsprintf(ach, szHexFmt, spy_WndClassEx.lpfnWndProc);

Как вы видите, это разные значения (полученные по-разному). Код для заполнения spy_WndProc находится в WinSpy.c и GetRemoteWindowInfo.c. Извлеченный код из GetRemoteInfo() в WinSpy.c:

GetClassInfoEx(0, spy_szClassName, &spy_WndClassEx);
GetRemoteWindowInfo(hwnd, &spy_WndClassEx, &spy_WndProc, spy_szPassword, 200);

Теперь в GetRemoteWindowInfo() мы видим вызов GetClassInfoExProc (введенный в другой процесс):

pInjData->wndproc = (WNDPROC)pInjData->fnGetWindowLong(pInjData->hwnd, GWL_WNDPROC);
pInjData->fnGetClassInfoEx(pInjData->hInst,
 (LPTSTR)pInjData->szClassName, &pInjData->wcOutput);

Как вы можете видеть (пожалуйста, используйте исходный код) wcOutput - это то, что отображается на вкладке "Класс" и WNDPROC, что отображается на вкладке "Общие". Просто GetWindowLong() выходит из строя, но GetClassInfoEx не выполняет (но они не обязательно получают одно и то же значение, потому что (если я не ошибаюсь) то, что у вас есть в WNDCLASSEX, это то, что вы зарегистрировали с помощью RegisterClassEx, но то, что вы получаете с GetWindowLong() это то, что вы подключили с помощью SetWindowLong().


Вы правы. Он не имеет функции WndProc (...). Это просто использование DlgProc для обработки событий диалога. Я теперь это, так как я написал код "сервер/тонкий клиент" в C/С++ для захвата прямых вызовов в функции API Windows, такие как WndProc (...). Любая функция графического интерфейса Windows действительно - BeginPaint (...) в качестве примера. Я использовал CALC.EXE в качестве тестового и исполняемого запуска на сервере, тогда как вызовы GUI передаются/возвращаются/от тонкого клиента. Проверяли только версии calc.exe через Vista. Существует вероятность, что новые версии были "запрограммированы" по-другому, а это означает, что Win32 SDK не используется. Но даже MFC - это всего лишь оболочка для Win32 SDK,

licensed under cc by-sa 3.0 with attribution.