Здравствуйте, Столкнулся с проблемой получения раскладки клавиатуры чужого консольного приложения в Vista. В 2000/XP работает через глобальный хук WH_SHELL с отловом HSHELL_LANGUAGE. В висте хук для консольного приложения не вызывается (насколько я понимаю, microsoft не гарантировал работу хука для консольных приложений даже в 2k/XP). Смотрел "известные" исходники программы internat, там сделано через хук. Подскажите пожалуйста, какие еще существуют варианты узнать раскладку чужого консольного приложения? Спасибо.
В консольной подсистеме есть недокументированный сервис ConsolepGetLangId, для XPSP2 его код 0x20251. Соответствующая апи есть GetConsoleLangId(), но не экспортируется и вызывается она при аттаче к консоле из kernel32!AttachConsole. Структура сообщения: Код (Text): typedef struct _CONSOLE_LANGID_MSG { IN HANDLE ConsoleHandle; OUT LANGID LangId; } CONSOLE_LANGID_MSG, *PCONSOLE_LANGID_MSG; typedef struct _CONSOLE_API_MSG { PORT_MESSAGE h; PCSR_CAPTURE_HEADER CaptureBuffer; CSR_API_NUMBER ApiNumber; ULONG ReturnValue; ULONG Reserved; CONSOLE_LANGID_MSG GetConsoleLangId; } CONSOLE_API_MSG, *PCONSOLE_API_MSG; Следует просто вызвать CsrClientCallServer(), передав указатель на CONSOLE_API_MSG, указав ApiNumber = ConsolepGetLangId и GetConsoleLangId.ConsoleHandle, последний получается аттачем к консоле. В висте номер сервиса наверно другой.
Спасибо за отличную идею! Код, который компилируется, написал На данном этапе CsrClientCallServer после вызова возвращает ReturnValue = 0xc0000008. Тестировал на русской XPSP2 Возникло еще пару вопросов. 1. Я правильно понимаю, что ConsoleHandle активного окна получать нужно так: DWORD dwProcessId; GetWindowThreadProcessId(GetForegroundWindow(), &dwProcessId); AttachConsole(dwProcessId); HANDLE hConsoleHandle = GetStdHandle(STD_INPUT_HANDLE); ? или имеется ввиду какой-то другой дескриптор? 2. Можно как-то программно узнавать код ConsolepGetLangId ? И в целом, какая методика определения этого ApiNumber ? Большое спасибо.
Возвращает STATUS_INVALID_HANDLE. Код из сорсов винды: Код (Text): #define GET_CONSOLE_HANDLE ((NtCurrentPeb())->ProcessParameters->ConsoleHandle) BOOL WINAPI GetConsoleLangId( OUT LANGID *lpLangId ) /*++ Parameters: lpLangId - Supplies a pointer to a LANGID in which to store the Language ID. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { CONSOLE_API_MSG m; PCONSOLE_LANGID_MSG a = &m.u.GetConsoleLangId; a->ConsoleHandle = GET_CONSOLE_HANDLE; CsrClientCallServer( (PCSR_API_MSG)&m, NULL, CSR_MAKE_API_NUMBER( CONSRV_SERVERDLL_INDEX, ConsolepGetLangId ), sizeof( *a ) ); if (NT_SUCCESS( m.ReturnValue )) { try { *lpLangId = a->LangId; } except( EXCEPTION_EXECUTE_HANDLER ) { return FALSE; } return TRUE; } else { return FALSE; } } Хэндл консоли - это значение из RTL_USER_PROCESS_PARAMETERS.ConsoleHandle. В XPSP2 одна ссылка в kernel32 на GetConsoleLangId, из SetTEBLangID(), эта также не экспортируется, но она сохраняет значение: Код (Text): VOID SetTEBLangID( VOID ) /*++ Sets the Language Id in the TEB to Far East if code page CP are Japanese/Korean/Chinese. This is done in order for FormatMessage to display any Far East character when cmd is running in its code page. All messages displayed in non-FE code page will be displayed in English. --*/ { LANGID LangId; if (GetConsoleLangId(&LangId)) { SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) ); } } Можно попробовать вызвать GetThreadLocale() после аттача к консоли, либо что тоже, считать из TEB поле CurrentLocale. Ссылки на SetTEBLangID() четыре. Наиболее прост вызов: Код (Text): BOOL WINAPI SetConsoleOutputCP( IN UINT wCodePageID ) /**++ Parameters: wCodePageID - the code page is to set for the current console output. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; Status = SetConsoleOutputCPInternal(wCodePageID); if(NT_SUCCESS(Status)) { SetTEBLangID(); return TRUE; } else { SET_LAST_NT_ERROR (Status); return FALSE; } } Смотри, в сорсах всё есть, либо под дизасмом. Получить код сервиса можно двумя способами. 1. Пройтись дизасмом длин, раскрыв два вложенных вызова процедуры(SetConsoleOutputCP->SetTEBLangID->GetConsoleLangId), из последней функции извлечь код. 2. Отследить запрос к серверу(CsrClientCallServer). Тут несколько вариантов: > Перехватить CsrClientCallServer() и считать параметры при вызове. > Трассировать процедуру, при вызове CsrClientCallServer() считать параметры. > Трассировать AttachConsole() и обрабатывать стековые фреймы, дабы отследить последовательность вызова. > Отследить SetThreadLocale(), либо запись в TEB.CurrentLocale Все способы просто реализуются. GetStdHandle возвращает значение поля RTL_USER_PROCESS_PARAMETERS.StandardOutput, а нужно поле ConsoleHandle. Считай вручную.