我不喜欢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

最佳答案

简而言之(关于您的代码):GetWindowLong()失败,因为您试图读取目标进程地址空间中的地址。

EXPLANATION

GetWindowLong()返回0时,表示存在来自MSDN的错误:



检查Marshal.GetLastWin32Error(),您可能会看到错误代码是ERROR_ACCESS_DENIED(数字值为0x5)。

为什么?因为GetWindowLong()试图获取窗口过程的地址(或句柄)(不在您的代码中,而是在目标过程中,理论上它甚至可能是默认窗口过程,但我从未见过不依赖于此的应用程序主窗口最少的消息)。您可以使用此技巧(但我从未尝试过!)来查看窗口是否正在使用默认过程(您是否有地址),我不知道...应该尝试。

现在想想WNDPROC是什么:

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

地址(在过程A中有效)在过程B中(在此毫无意义)是不可调用的。 Windows DLLs代码段跨进程共享(我假设,我没有检查,但是在安全性和性能之间是合理的)。

此外,CallWindowProc(NULL, ...)将理解NULL作为调用该窗口类的窗口过程的特殊值(在HWND所有者上)。从MSDN:



Microsoft Spy++是如何做到的(也许WinSpy++没有)?没有WinSpy++源代码很难说。当然,这不是像GetWindowLong()这样简单的方法,正确的方法应该包含CreateRemoteThread()并从中进行LoadLibrary()的处理,但是Microsoft Spy++和WinSpy++源代码都不可用(AFAIK)进行进一步检查...

更新

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's 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.cGetRemoteWindowInfo.c中。从GetRemoteInfo()中的WinSpy.c中提取的代码:
GetClassInfoEx(0, spy_szClassName, &spy_WndClassEx);
GetRemoteWindowInfo(hwnd, &spy_WndClassEx, &spy_WndProc, spy_szPassword, 200);

现在在GetRemoteWindowInfo()中,我们看到了对GetClassInfoExProc的调用(在另一个过程中注入(inject)):
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()挂钩的内容。

09-28 04:23