Доброго, уважаемые. Тема такая, заколустало ошибаться при вводе и переключаться туда-обратно с одной раскладки на другую. Пункто свичеры всякие я не люблю. Я хочу сам написать литтл прогу, которая имела бы прозрачное окно и висело постоянно на экране в виде флажка текущей раскладки. Пошуршал по инету. Нашёл как делается "перехват" переключения раскладки. Типа так, к примеру: Код (Text): procedure TfrmMain.Timer1Timer(Sender: TObject); var FID: Cardinal; begin FID := GetWindowThreadProcessId(GetForegroundWindow, nil); AttachThreadInput(GetCurrentThreadId, FID, True); Caption := IntToStr(GetKeyboardLayout(FID) and $FFFF); AttachThreadInput(GetCurrentThreadId, FID, False); end; Судя по MSDN: Поэтому таймер некоторые и используют. Чего-то решение с таймером мне не очень нравится, да и аттачинг что-то тоже смахивает на грубую силу. Нет ли поэлегантнее метода в фоне отслеживания текущей раскладки чужого приложения. Чтобы перед тем как писать что-то - смотришь на экран - там висит флажок и знаешь надо или не надо переключаться. Мне нужна именно такая тулза, без прочих наваротов. Стик на экране. Спасибо за внимание. Вышеприведённый код ещё пока не пробовал. Пока буду с ним дело иметь.
Есть, дизассмил win32k.sys там есть неэкспортируемая функция которая позволяет получить текущую раскладку, раскладка хранится в локальной переменной и меняется всегда на текущую раскладку выбранного окна. Можешь как нить поискать такую функцию. Название функции вроде GetActiveHKL(если pdbdump не врет), но она не экспортируемая, по хэшу найти думаю проблемы не будет, хотя я могу ошибаться так как подробно пробемой этой не интересовался.
Это уже интересней. Спасибо, будем смотреть. А пока, может укажите кто-нить мне на ошибки. Набросал тут "рыбу" на будущую программу по вышеуказанному методу. Фурычит, но увы только при её же активном окне. Что делать? Где я не дочитал MSDN? Заголовочник windows.h не забудьте добавить, остальное по дефолту. Код (Text): // ShowLang.cpp : Defines the entry point for the application. // #include "stdafx.h" #define ID_TIMER_1 100 #define TIME_DELAY 100 char text_ru[ ] = "RU"; char text_en[ ] = "EN"; char ClassName[] = "cMyApp1"; char AppName[] = "ShowLang"; HWND hMyWnd; MSG msg; UINT nIdTimer; void CALLBACK OnTimerTick (HWND, UINT, UINT, DWORD); LRESULT WndProc (HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); // заполнение структуры wc wc.style = CS_HREDRAW || CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hbrBackground = HBRUSH (COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = ClassName; wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION); wc.hIconSm = wc.hIcon; wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); ::RegisterClassEx(&wc); // регистрация нашего класса окна hMyWnd = ::CreateWindowEx(NULL, ClassName, AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); // Создаём таймер и устанавливаем тик на TIME_DELAY мсек nIdTimer = ::SetTimer(hMyWnd, ID_TIMER_1, TIME_DELAY, OnTimerTick); ::ShowWindow(hMyWnd, nCmdShow); // отобразить наше окно на десктопе ::UpdateWindow(hMyWnd); // обновить клиентскую область while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // Тикаем по-тихоньку void CALLBACK OnTimerTick ( HWND hWnd, // дескриптор CWND который вызвал SetTimer UINT nMsg, // WM_TIMER UINT nIDEvent, // идентификатор таймера DWORD dwTime // системное время ) { DWORD FId; RECT r; HDC hdc; hdc = ::GetDC(hWnd); ::GetClientRect(hWnd, &r); // Проверяем раскладку клавиатуры у активного окна и показываем // соотв-щую картинку FId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL); ::AttachThreadInput(::GetCurrentThreadId(), FId, TRUE); // Send some text out into the world switch ((DWORD)::GetKeyboardLayout(FId) & 0xFFFF) { // Раскладка английская case 0x0409: { ::TextOut(hdc, r.left, r.top, text_en, 2); break; } // Раскладка русская case 0x0419: { ::TextOut(hdc, r.left, r.top, text_ru, 2); break; } } ::AttachThreadInput(::GetCurrentThreadId(), FId, FALSE); } LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_DESTROY: ::KillTimer(hWnd, nIdTimer); ::PostQuitMessage(NULL); return 0; break; }; return DefWindowProc(hWnd, uMsg, wParam, lParam); }; P.S. Поправил пару неточностей. Видимо аттачинг как-то не так как хотелось бы работает. Не те функции что-ли. Придётся читать, с ходу не получилось. P.S2. Извиняюсь, был не внимателен. Всё работает. Рыбу можно доделывать до чего-нить стоящего. Щаз картинки вставлю, AlwaysOnTop пропишу, титл у окна уберу и сделаю перетаскивание мышкой. Останется только в инишник текущие координаты скидывать сделать и прозрачность. Может у кого есть код какой, чтобы велосипед не изобретать? А пунктросвичеры я поубирал с тех пор, как однажды не смог через удалённый терминал зайти на линукс систему. Раз вхожу, не входит, второй третий...ё-маё ...думаю что за фигня. Потом дошло, что этот гад пароль меняет, а его то не видать... кошмар. Медвежья услуга. Как я до этого допёр, так и удалил его нафиг.
Ну может он отключил и теперь не знает как включить =) PS: Я убрал этот значек, только место на мониторе занимает.
Вот код работающей утилитки, прям того что я хотел: Код (Text): // ShowLang.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "Resource.h" #define ID_TIMER_1 100 #define TIME_DELAY 100 #ifndef WS_EX_LAYERED #define WS_EX_LAYERED 0x00080000 #define LWA_COLORKEY 0x00000001 #define LWA_ALPHA 0x00000002 #endif // ndef WS_EX_LAYERED #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif char ClassName[] = "cMyApp3"; char AppName[] = "ShowLang"; HWND hMyWnd; MSG msg; UINT nIdTimer; // Картинки HBITMAP bmRU, bmEN; POINT point, point2; BYTE CurrentAlpha; // Preparation for the function we want to import from USER32.DLL typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hWnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags); lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes; void CALLBACK OnTimerTick (HWND, UINT, UINT, DWORD); LRESULT WndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK ParentWindowProc (HWND, UINT , UINT, LONG); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; RECT r; wc.cbSize = sizeof(WNDCLASSEX); // заполнение структуры wc wc.style = CS_HREDRAW || CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hbrBackground = HBRUSH (COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = ClassName; wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION); wc.hIconSm = wc.hIcon; wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); ::RegisterClassEx(&wc); // регистрация нашего класса окна hMyWnd = ::CreateWindowEx(WS_EX_TOPMOST, ClassName, AppName, WS_POPUP, 0, 0, 29, 19, NULL, NULL, hInstance, NULL ); // Создаём таймер и устанавливаем тик на TIME_DELAY мсек nIdTimer = ::SetTimer(hMyWnd, ID_TIMER_1, TIME_DELAY, OnTimerTick); ::GetClientRect(hMyWnd, &r); HRGN hRgn1 = ::CreateRectRgnIndirect(&r); // ::SetWindowRgn(hMyWnd, hRgn1, TRUE); // Here we import the function from USER32.DLL HMODULE hUser32 = GetModuleHandle("USER32.DLL"); m_pSetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress(hUser32, "SetLayeredWindowAttributes"); // If the import did not succeed, make sure your app can handle it! if (NULL == m_pSetLayeredWindowAttributes) return FALSE; //Bail out!!! // Check the current state of the dialog, and then add the WS_EX_LAYERED attribute ::SetWindowLong(hMyWnd, GWL_EXSTYLE, ::GetWindowLong(hMyWnd, GWL_EXSTYLE) | WS_EX_LAYERED); // Sets the window to 70% visibility. CurrentAlpha = 120; m_pSetLayeredWindowAttributes(hMyWnd, 0, CurrentAlpha, LWA_ALPHA); // Подгрузили картинки, далее будем их менять bmRU = ::LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP_RU)); bmEN = ::LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP_EN)); // для начала зарегестрируем отдельный класс для "скрытого" родителя. // Можно и не делать этого, а воспользоваться классом главного онка! WNDCLASSEX WndClass; char szClassName[] = "HiddenWindow"; WndClass.style = CS_HREDRAW | CS_VREDRAW; WndClass.lpfnWndProc = (WNDPROC)ParentWindowProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = hInstance; WndClass.hIcon = 0l; WndClass.hCursor = NULL; WndClass.hbrBackground = (HBRUSH) GetStockObject (GRAY_BRUSH); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = szClassName; WndClass.hIconSm = 0l; if (!RegisterClassEx(&WndClass)) { if (!RegisterClass((LPWNDCLASS)&WndClass.style)) { MessageBox(NULL, "Can't register the class", "Error", MB_APPLMODAL | MB_ICONSTOP); return 0; // или "return 0;" Смотря что надо вернуть } } // Теперь надо создать по этому классу окно HWND hWndParent = CreateWindow (szClassName, "Hidden Window by SUnteXx", WS_THICKFRAME | WS_BORDER | WS_EX_TOOLWINDOW, -31000, -31000, -30900, -30900, NULL, NULL, hInstance, NULL); // Теперь необходимо поставить родителя нашему главному окошку ; LONG l = SetWindowLong(hMyWnd, GWL_HWNDPARENT, (LONG)hWndParent); // l будет равен указателю на старый родитель (или 0)! // Если главное окно создаем после создания "скрытого" окна, то можно сразу при создании указать нашего родителя! ::ShowWindow(hMyWnd, nCmdShow); // отобразить наше окно на десктопе ::UpdateWindow(hMyWnd); // обновить клиентскую область while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // Тикаем по-тихоньку void CALLBACK OnTimerTick ( HWND hWnd, // дескриптор CWND который вызвал SetTimer UINT nMsg, // WM_TIMER UINT nIDEvent, // идентификатор таймера DWORD dwTime // системное время ) { DWORD FId; HDC hdc, hMemDC; hdc = ::GetDC(hWnd); // Создаем контекст памяти, совместимый с контекстом отображения hMemDC = ::CreateCompatibleDC(hdc); // Проверяем раскладку клавиатуры у активного окна и показываем // соотв-щую картинку FId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL); ::AttachThreadInput(::GetCurrentThreadId(), FId, TRUE); // Send some text out into the world switch ((DWORD)::GetKeyboardLayout(FId) & 0xFFFF) { // Раскладка английская case 0x0409: { //::TextOut(hdc, r.left, r.top, text_en, 2); //::GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmEN); ::SelectObject(hMemDC, bmEN); break; } // Раскладка русская case 0x0419: { // ::TextOut(hdc, r.left, r.top, text_ru, 2); //::GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmRU); ::SelectObject(hMemDC, bmRU); break; } } ::BitBlt(hdc, 0, 0, 29, 19, hMemDC, 0, 0, SRCCOPY); // Удаляем контекст памяти ::DeleteDC(hMemDC); ::ReleaseDC(hWnd, hdc); ::AttachThreadInput(::GetCurrentThreadId(), FId, FALSE); } LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_DESTROY:{ ::KillTimer(hWnd, nIdTimer); ::DeleteObject(bmRU); // delete bitmaps ::DeleteObject(bmEN); ::PostQuitMessage(NULL); return 0; break; } case WM_LBUTTONUP:{ if (::GetCapture() == hWnd) ::ReleaseCapture(); break; } case WM_LBUTTONDOWN:{ SetCapture(hWnd); point.x = LOWORD(lParam); point.y = HIWORD(lParam); break; } case WM_MOUSEMOVE:{ if(::GetCapture() == hWnd){ if(::GetAsyncKeyState(VK_LBUTTON)){ ::GetCursorPos(&point2); RECT wRect; ::ScreenToClient(hWnd, &point2); int ccx = point2.x-point.x; int ccy = point2.y-point.y; ::GetWindowRect(hWnd, &wRect); ::MoveWindow(hWnd, wRect.left+ccx, wRect.top+ccy, wRect.right-wRect.left, wRect.bottom-wRect.top,TRUE); } } break; } case WM_MOUSEWHEEL:{ if(HIWORD(msg.wParam) <= 32512) { CurrentAlpha += 20; m_pSetLayeredWindowAttributes(hMyWnd, 0, CurrentAlpha, LWA_ALPHA); } else { CurrentAlpha -= 20; m_pSetLayeredWindowAttributes(hMyWnd, 0, CurrentAlpha, LWA_ALPHA); }; } } return DefWindowProc(hWnd, uMsg, wParam, lParam); }; LRESULT CALLBACK ParentWindowProc (HWND hWnd, UINT uMessage, UINT wParam, LONG lParam) { return DefWindowProc(hWnd, uMessage, wParam, lParam); } Как пользовать исходник. Для компиляции добавьте 2 картинки (BITMAP) в ресурсы. Картинки я взял с сайта cracklab.ru - там на главнной странице посмотрите справа вверху. Вот их и грузите и конвертите в bmp, потом в ресуры. Идентификаторы посмотрете в исходнике. Всё. Компилите и запускатете. Появится мелкий флаг. В исходниках можно настраивать время тикания таймера. Флаг можно таскать по экрану за него. Пара вопросов. Первое, почему-то отображение окон немного затормаживается при запущеной проге. Как пофиксить? В чём дело? Второе, когда хоу сделать прозрачность то ни MSVC6 ни MSVC2005Pro не находят идентификатора WS_EX_LAYERED и LWA_ALPHA и самой SetLayeredWindowAttributes(), кроме того двойной клик почему-то не выделяет слово в редакторе =) - что делать? А так вполне юзабельная прога. P.S. Ну, ребят, не смейтесь, если я такую прогу пишу, то УЖ НАВЕРНОЕ знаю про трей. У меня 2-е девятнашки монитора стоят. Причём второй слева от основного. У меня таскбар сверху и выпадающий, мне так удобнее... такие настройки уже вного много лет. Так вот при расширенном десктопе таск бар только на одном показывается. Да и все равно я экономлю место в трее, а кроме того там сворачиваются иконки. Так что я знаю чего я хочу. P.S2. Так, походу я зыбал сделать ::ReleaseDC(hWnd, hdc); Без него утечка ресурсов была. P.S3. Надо переписывать на асме, он не станет мне указывать на незнание им функций прозрачности. Как напишу, так и будет. P.S4. На асме как-нить потом. Добавил пару фичей. Теперь окна нет на таскбаре и можно запустить по нескольку штук и расположить хоть по всем углам. Второе - колесиком мышки при наведении меняется прозрачность. В системах Win9x будет не находить функцию установки прозрачности. А кто сейчас в них работает? Поставте условие сами в нужных местах тогда, либо если обновлённый SDK, то используйте отложенную загрузку всесте с проверкой ОС. Усё тему можно считать исчерпаной. Разве что ini файл приделать с сохранением Placement.
Нет, не исчерпана тема. Программа почему-то не реагирует на переключение раскладки в Far'е, т.е. на консоль похоже не реагирует. Дела.
Ребят, может меня кто просветит? Нужно, чтобы приаттаченная тулза (zip, ~53 Kb, MSDev Studio 6 project) не маскировала двойное нажатие на левую клавишу мыши. Когда она запущена, то нельзя двойным кликом пользоваться и я не пойму пока как происходит маскирование. Как пользоваться тулзой: 1) Появляется при загрузке в верхнем левом углу экрана 2) Еле видима, чтобы уменьшить/увеличить прозрачность крутим колесиком, когда активна 3) Воспроизводит непонятные звуки, которые отдалённо напоминают урезанные "русская" и "английская", то бишь раскладка. WAV'ы в ресурсах. 4) Таскаем за флаг, чтобы изменить месторасположение Обычно я её по центру экрана ставлю и делаю прозрачной. Вроде и не мешает и сразу видать раскладку. To Do: 1) Автоматическое центрирование посередине экрана (может кто скажет как это сделать с учётом расширенного на несколько мониторов рабочего стола) 2) Добавка всплывающего меню с какими-нить опциями 3) Клонирование окошек, а не запуск отдельных программ 4) Отслеживание раскладки в консоли (чем она такая особенная?) Прошу прощения сразу за не слишком документированный код. Если кратко о коде: - установлен таймер и написана callback фукнция для него, в которой происходит подключение к текущему процессу и узнавание его локали; - набор фич разных - перетаскивание за окно, обрезание окна и сокрытие с панели задач. Никакой объектности, чистый Win32API. Посоветуйте чего-нить. Без двойного клика - это не жизнь. P.S. Ёлки. Видимо я не дорос ещё, чтобы аттачи делать. [zip, ~53 Kb, http://slil.ru/23637783] P.S2. Я думаю, что всё дело в реализации перетаскивания. Пока не могу осилить как там теряется двойной клик. Если б сам код писал... а то повыдергивал с разных мест. Может просто кто-то сталкивался? Сам думаю пока, что не так... читаю MSDN по *Capture() P.S3. Нет, я ошибся, после отключения частей кода выяснилось, что грешит тут конструкция: Код (Text): ::AttachThreadInput(::GetCurrentThreadId(), FId, TRUE); newhkl = ::GetKeyboardLayout(FId); ::AttachThreadInput(::GetCurrentThreadId(), FId, FALSE); Неужели так можно маскировать двойной клик, а может и ещё чего? Видимо я портачу где-то с параметрами или частотой вызова или ещё чего в данном куске кода. P.S4. Видимо эта строка из MSDN есть ответ на вопрос (статья про AttachThreadInput): Видимо я каждые энцать сотен миллисекунд просто напросто ресечу входной поток с клавы/мыши. Т.е. двойной клик видимо просто не успевает обработаться. Ёклмн, что же делать? P.S5. Что делать... походу эта функция аттачинга вообще не нужна. Когда выбрасываем AttachThreadInput() всё работает вроде также, но с двойным кликом. Всем спасибо. Наверное разобрался. Вот так бы всегда. Зашёл на форум и сразу разобрался, надо почаще на форумы заходить.