Spy ++ показывает неправильные результаты?

Я создал простое окно Unicode, и я нажал клавишу на клавиатуре, чтобы узнать, что будет означать значение wParam для сообщения WM_CHAR, и это дало мне, как ожидалось, код кода Unicode для символа, я нажал 'S', а моя раскладка клавиатуры была установлена ​​на арабский (так что арабский символ - 'س').

Теперь я также захватил оконные сообщения в Spy ++, но я заметил, что это дало мне неправильное значение для wParam, оно фактически дало мне значение для символьного кода на кодовой странице Windows: арабский!

это скриншот результатов:

enter image description here

и это исходный код:

#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;
}

Ответ 1

Как работает Spy ++ - это некая публичная тайна, вы можете легко сказать, когда вы запускаете Dumpbin.exe /imports в .exe файле. Для spyxx_amd64.exe(64-разрядная версия) наиболее важными элементами являются:

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, которая вводится в каждый процесс, содержит обратные вызовы hook.

Обратите внимание, что он использует как Unicode, так и Ansi-версию SetWindowsHookEx(). Один из способов, которым можно легко объяснить, - это когда он использует версию Ansi (SetWindowsHookExA) в вашем окне Unicode. Такой крючок может наблюдать только версию Ansi сообщения WM_CHAR.

Имейте в виду, что Spy ++ не может решить проблему, когда вы запускаете процессы на своем рабочем столе, которые содержат окна Unicode и Ansi, что не является чем-то необычным, и не может сохранить их всех счастливыми. Самое простое предположение состоит в том, что он просто пьет для SetWindowsHookExA как самый низкий общий знаменатель.

Фактически доказательство того, что Spy ++ получает это неправильно, намного сложнее сделать, и я долгое время его откладывал. Любая попытка поймать Spy ++, устанавливая крючки при ее использовании, оказалась бюстом, крючки устанавливаются очень рано при запуске программы. Метод отладки, который я в конце концов обнаружил:

  • Сначала я нашел исходную точку входа api NtUserSetWindowHookEx, разобрав код для SetWindowsHookExW. На моей машине (Win8.1) она находится на 0x00007FFECC3BA970.
  • Запущен VS повышенный уровень, необходимый для запуска Spy ++.
  • Файл + Открыть + Проект/Решение и выберите Spyxx_amd64.exe.
  • Отладка + шаг. Это запускает программу и находит точку выполнения в AfxWinMain (Spy ++ был написан в библиотеке MFC).
  • Отладка + Windows + Разборка, вставьте 0x00007FFECC3BA970 в поле адреса
  • Установите точку останова по этому адресу. Нажмите F5

Есть два ложных удара, MFC использует SetWindowsHookExW() внутри. Но затем он загорается, три удара, которые выглядят примерно так:

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 ++ может отображать только версию Ansi сообщения WM_CHAR.

Ответ 2

Возможно, у вас есть арабская кодовая страница по умолчанию в региональных настройках, а Spy ++ отображает те же значения, что и в программе без поддержки Unicode? Просто дикое предположение, хотя.