首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Spy++显示错误的结果?

Spy++显示错误的结果?
EN

Stack Overflow用户
提问于 2014-11-05 02:40:00
回答 2查看 1K关注 0票数 10

我创建了一个简单的Unicode窗口,我在键盘上按了一个键,查看wParam对于WM_CHAR消息的值是什么,它给了我这个字符的Unicode代码点,我按了'S‘键,我的键盘布局被设置为阿拉伯语(所以阿拉伯字符是'س')。

现在,我还捕获了Spy++中的窗口消息,但是我注意到它给了我一个错误的wParam值,它实际上给了我Windows中字符代码的值:阿拉伯代码页!

这是结果的截图:

这是源代码:

代码语言:javascript
复制
#define UNICODE

#include <Windows.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_CHAR:
        char str[256];
        sprintf(str, "0x%.4x", wParam);
        MessageBoxA(NULL, str, "", 0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"WinClass";
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    RegisterClassEx(&wc);

    HWND hWnd = CreateWindowEx(0, L"WinClass", L"My Title", WS_OVERLAPPEDWINDOW, 261, 172, 594, 384, NULL, NULL, hInstance, NULL);
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-03-09 23:50:58

Spy++的工作方式是一个公开的秘密,您可以很容易地知道什么时候在.exe文件上运行Dumpbin.exe /imports。对于spyxx_amd64.exe (64位版本),最相关的条目是:

代码语言:javascript
复制
SPYXXHK_AMD64.DLL
   ...
                       3 SpyxxCallWndRetProc
                       2 SpyxxCallWndProc
                       4 SpyxxGetMsgProc
USER32.dll
   ...
                     320 SetWindowsHookExW
                     31F SetWindowsHookExA

换句话说,它使用SetWindowsHookEx()来设置3个钩子、WH_CALLWNDPROC、WH_CALLWNDPROCRET和WH_GETMESSAGE。Spyxxhk_amd64.dll是注入到每个进程中的DLL,它包含钩子回调。

请注意,它同时使用了SetWindowsHookEx()的Unicode和Ansi版本。您的结果可以很容易地解释的一种方法是在您的Unicode窗口中使用Ansi版本(SetWindowsHookExA)。这样的钩子只能观察WM_CHAR消息的Ansi版本。

请记住,当您在包含Unicode和Ansi窗口的桌面上运行进程时,Spy++有一个不可能解决的问题,这并不少见,但并不能使它们都满意。最简单的假设是,它只是将SetWindowsHookExA作为最低公分母。

实际上,要证明Spy++做错了这件事要困难得多,我花了很长一段时间。任何试图捕捉Spy++在使用钩子时安装钩子的尝试都是失败的,这些钩子是在程序启动时很早就安装的。我最终发现的调试技巧:

  • 我首先通过分解NtUserSetWindowHookEx的代码来定位本地api入口点SetWindowsHookExW。在我的机器上(Win8.1),它位于0x00007FFECC3BA970。
  • 启动和提升,需要启动Spy++。
  • 文件+打开+项目/解决方案并选择Spyxx_amd64.exe。
  • 调试+进入。这将启动程序并在AfxWinMain处定位执行点(Spy++是用MFC库编写的)。
  • 调试+ Windows +反汇编,将0x00007FFECC3BA970粘贴到地址栏中
  • 将断点设置在此地址。按F5

有两个错误点击,MFC在内部使用SetWindowsHookExW()。但后来它亮了起来,三次点击看上去都与此相似:

代码语言:javascript
复制
user32.dll!NtUserSetWindowsHookEx()     
user32.dll!_SetWindowsHookEx()  + 0x5b bytes    
user32.dll!SetWindowsHookExAW()  + 0x5b bytes   
user32.dll!SetWindowsHookExA()  + 0x11 bytes    
spyxx_amd64.exe!SetMsgHook()  + 0x6a bytes  
spyxx_amd64.exe!HookMain()  + 0x470 bytes   
msvcr120.dll!_callthreadstart()  Line 257   C
msvcr120.dll!_threadstart(void * ptd)  Line 237 + 0x5 bytes C
kernel32.dll!BaseThreadInitThunk()  + 0xd bytes 
ntdll.dll!RtlUserThreadStart()  + 0x34 bytes    

这就是证据,您可以看到SetWindowsHookExA()正在被调用。在Ansi版本中,Spy++只能显示WM_CHAR消息的Ansi版本。

票数 8
EN

Stack Overflow用户

发布于 2015-03-09 15:24:43

也许您在“区域设置”中将阿拉伯语代码页设置为默认设置,而Spy++显示的值与在没有Unicode支持的程序中显示的值相同?不过,只是胡思乱想。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26748851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档