Текущая раскладка [WM_INPUTLANGCHANGEREQUEST]

Тема в разделе "WASM.WIN32", создана пользователем uni, 19 дек 2006.

  1. uni

    uni New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    67
    Доброго, уважаемые.
    Тема такая, заколустало ошибаться при вводе и переключаться туда-обратно с одной раскладки на другую. Пункто свичеры всякие я не люблю. Я хочу сам написать литтл прогу, которая имела бы прозрачное окно и висело постоянно на экране в виде флажка текущей раскладки. Пошуршал по инету. Нашёл как делается "перехват" переключения раскладки. Типа так, к примеру:
    Код (Text):
    1. procedure TfrmMain.Timer1Timer(Sender: TObject);
    2. var
    3.   FID: Cardinal;
    4. begin
    5.   FID := GetWindowThreadProcessId(GetForegroundWindow, nil);
    6.   AttachThreadInput(GetCurrentThreadId, FID, True);
    7.   Caption := IntToStr(GetKeyboardLayout(FID) and $FFFF);
    8.   AttachThreadInput(GetCurrentThreadId, FID, False);  
    9. end;
    Судя по MSDN:
    Поэтому таймер некоторые и используют. Чего-то решение с таймером мне не очень нравится, да и аттачинг что-то тоже смахивает на грубую силу. Нет ли поэлегантнее метода в фоне отслеживания текущей раскладки чужого приложения. Чтобы перед тем как писать что-то - смотришь на экран - там висит флажок и знаешь надо или не надо переключаться. Мне нужна именно такая тулза, без прочих наваротов. Стик на экране. Спасибо за внимание. Вышеприведённый код ещё пока не пробовал. Пока буду с ним дело иметь.
     
  2. Guest

    Guest Guest

    Публикаций:
    0
    Есть, дизассмил win32k.sys там есть неэкспортируемая функция которая позволяет получить текущую раскладку, раскладка хранится в локальной переменной и меняется всегда на текущую раскладку выбранного окна. Можешь как нить поискать такую функцию. Название функции вроде GetActiveHKL(если pdbdump не врет), но она не экспортируемая, по хэшу найти думаю проблемы не будет, хотя я могу ошибаться так как подробно пробемой этой не интересовался.
     
  3. uni

    uni New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    67
    Это уже интересней. Спасибо, будем смотреть. А пока, может укажите кто-нить мне на ошибки. Набросал тут "рыбу" на будущую программу по вышеуказанному методу. Фурычит, но увы только при её же активном окне. Что делать? Где я не дочитал MSDN? Заголовочник windows.h не забудьте добавить, остальное по дефолту.
    Код (Text):
    1. // ShowLang.cpp : Defines the entry point for the application.
    2. //
    3.  
    4. #include "stdafx.h"
    5.  
    6. #define ID_TIMER_1 100
    7. #define TIME_DELAY 100
    8.  
    9. char text_ru[ ] = "RU";
    10. char text_en[ ] = "EN";
    11.  
    12. char ClassName[] = "cMyApp1";
    13. char AppName[] = "ShowLang";
    14. HWND hMyWnd;
    15. MSG msg;
    16. UINT nIdTimer;
    17.  
    18. void CALLBACK   OnTimerTick (HWND, UINT, UINT, DWORD);
    19. LRESULT         WndProc     (HWND, UINT, WPARAM, LPARAM);
    20.  
    21. int APIENTRY WinMain(HINSTANCE hInstance,
    22.                      HINSTANCE hPrevInstance,
    23.                      LPSTR     lpCmdLine,
    24.                      int       nCmdShow)
    25. {
    26.  WNDCLASSEX wc;
    27.  
    28.  wc.cbSize = sizeof(WNDCLASSEX); // заполнение структуры wc
    29.  wc.style =  CS_HREDRAW || CS_VREDRAW;
    30.  wc.lpfnWndProc = (WNDPROC) WndProc;
    31.  wc.cbClsExtra = NULL;
    32.  
    33.  wc.cbWndExtra = NULL;
    34.  wc.hInstance = hInstance;
    35.  wc.hbrBackground = HBRUSH (COLOR_WINDOW + 1);
    36.  
    37.  wc.lpszMenuName = NULL;
    38.  wc.lpszClassName = ClassName;
    39.  
    40.  wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
    41.  wc.hIconSm = wc.hIcon;
    42.  
    43.  wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    44.  
    45.  ::RegisterClassEx(&wc); // регистрация нашего класса окна
    46.  
    47.  hMyWnd = ::CreateWindowEx(NULL, ClassName, AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
    48.                 CW_USEDEFAULT,
    49.                 CW_USEDEFAULT,
    50.                 CW_USEDEFAULT,
    51.                 NULL,
    52.                 NULL,
    53.                 hInstance,
    54.                 NULL
    55.                     );
    56. // Создаём таймер и устанавливаем тик на TIME_DELAY мсек
    57.  nIdTimer = ::SetTimer(hMyWnd, ID_TIMER_1, TIME_DELAY, OnTimerTick);
    58.  
    59.  ::ShowWindow(hMyWnd, nCmdShow); // отобразить наше окно на десктопе
    60.  ::UpdateWindow(hMyWnd); // обновить клиентскую область
    61.  
    62.  while(GetMessage(&msg, NULL, 0, 0))
    63.  {
    64.   TranslateMessage(&msg);
    65.   DispatchMessage(&msg);
    66.  }
    67.  
    68.  return msg.wParam;
    69. }
    70.  
    71. // Тикаем по-тихоньку
    72. void CALLBACK OnTimerTick
    73. (
    74.     HWND hWnd,  // дескриптор CWND который вызвал SetTimer
    75.     UINT nMsg,  // WM_TIMER
    76.     UINT nIDEvent,  // идентификатор таймера
    77.     DWORD dwTime    // системное время
    78. )
    79. {
    80.  DWORD FId;
    81.  RECT r;
    82.  HDC hdc;
    83.  
    84.  hdc = ::GetDC(hWnd);
    85.  ::GetClientRect(hWnd, &r);
    86.  
    87. // Проверяем раскладку клавиатуры у активного окна и показываем
    88. // соотв-щую картинку
    89.  FId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL);
    90.  ::AttachThreadInput(::GetCurrentThreadId(), FId, TRUE);
    91.  
    92. // Send some text out into the world
    93.  switch ((DWORD)::GetKeyboardLayout(FId) & 0xFFFF) {
    94. // Раскладка английская
    95.   case 0x0409: {
    96.     ::TextOut(hdc, r.left, r.top, text_en, 2);
    97.      break;
    98.               }
    99. // Раскладка русская
    100.   case 0x0419: {
    101.     ::TextOut(hdc, r.left, r.top, text_ru, 2);
    102.      break;
    103.               }
    104.  }
    105.  
    106.  ::AttachThreadInput(::GetCurrentThreadId(), FId, FALSE);
    107.  
    108. }
    109.  
    110. LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    111. {
    112.  switch(uMsg) {
    113.  
    114.  case WM_DESTROY:
    115.   ::KillTimer(hWnd, nIdTimer);
    116.   ::PostQuitMessage(NULL);
    117.   return 0;
    118.   break;
    119.  };
    120.  
    121.  return DefWindowProc(hWnd, uMsg, wParam, lParam);
    122. };
    P.S. Поправил пару неточностей. Видимо аттачинг как-то не так как хотелось бы работает. Не те функции что-ли. Придётся читать, с ходу не получилось.
    P.S2. Извиняюсь, был не внимателен. Всё работает. Рыбу можно доделывать до чего-нить стоящего. Щаз картинки вставлю, AlwaysOnTop пропишу, титл у окна уберу и сделаю перетаскивание мышкой. Останется только в инишник текущие координаты скидывать сделать и прозрачность. Может у кого есть код какой, чтобы велосипед не изобретать?

    А пунктросвичеры я поубирал с тех пор, как однажды не смог через удалённый терминал зайти на линукс систему. Раз вхожу, не входит, второй третий...ё-маё ...думаю что за фигня. Потом дошло, что этот гад пароль меняет, а его то не видать... кошмар. Медвежья услуга. Как я до этого допёр, так и удалил его нафиг.
     
  4. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Так текущая раскладка всю жизнь была видна в трее.
     
  5. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    Ну может он отключил и теперь не знает как включить =)
    PS: Я убрал этот значек, только место на мониторе занимает.
     
  6. atorn

    atorn New Member

    Публикаций:
    0
    Регистрация:
    14 дек 2006
    Сообщения:
    64
  7. uni

    uni New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    67
    Вот код работающей утилитки, прям того что я хотел:
    Код (Text):
    1. // ShowLang.cpp : Defines the entry point for the application.
    2. //
    3.  
    4. #include "stdafx.h"
    5. #include "Resource.h"
    6.  
    7. #define ID_TIMER_1 100
    8. #define TIME_DELAY 100
    9.  
    10. #ifndef WS_EX_LAYERED
    11. #define WS_EX_LAYERED   0x00080000
    12. #define LWA_COLORKEY        0x00000001
    13. #define LWA_ALPHA           0x00000002
    14. #endif // ndef WS_EX_LAYERED
    15.  
    16. #ifndef WM_MOUSEWHEEL
    17. #define WM_MOUSEWHEEL                   0x020A
    18. #endif
    19.  
    20. char ClassName[] = "cMyApp3";
    21. char AppName[] = "ShowLang";
    22. HWND hMyWnd;
    23. MSG msg;
    24. UINT nIdTimer;
    25. // Картинки
    26. HBITMAP bmRU, bmEN;
    27. POINT point, point2;
    28. BYTE CurrentAlpha;
    29.  
    30. // Preparation for the function we want to import from USER32.DLL
    31. typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hWnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
    32. lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
    33.  
    34. void CALLBACK   OnTimerTick (HWND, UINT, UINT, DWORD);
    35. LRESULT         WndProc     (HWND, UINT, WPARAM, LPARAM);
    36. LRESULT CALLBACK ParentWindowProc (HWND, UINT , UINT, LONG);
    37.  
    38. int APIENTRY WinMain(HINSTANCE hInstance,
    39.                      HINSTANCE hPrevInstance,
    40.                      LPSTR     lpCmdLine,
    41.                      int       nCmdShow)
    42. {
    43.  WNDCLASSEX wc;
    44.  RECT r;
    45.  
    46.  wc.cbSize = sizeof(WNDCLASSEX); // заполнение структуры wc
    47.  wc.style =  CS_HREDRAW || CS_VREDRAW;
    48.  wc.lpfnWndProc = (WNDPROC) WndProc;
    49.  wc.cbClsExtra = NULL;
    50.  
    51.  wc.cbWndExtra = NULL;
    52.  wc.hInstance = hInstance;
    53.  wc.hbrBackground = HBRUSH (COLOR_WINDOW + 1);
    54.  
    55.  wc.lpszMenuName = NULL;
    56.  wc.lpszClassName = ClassName;
    57.  
    58.  wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
    59.  wc.hIconSm = wc.hIcon;
    60.  
    61.  wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    62.  
    63.  ::RegisterClassEx(&wc); // регистрация нашего класса окна
    64.  
    65.  hMyWnd = ::CreateWindowEx(WS_EX_TOPMOST, ClassName, AppName, WS_POPUP,
    66.                     0, 0, 29, 19,
    67.                 NULL,
    68.                 NULL,
    69.                 hInstance,
    70.                 NULL
    71.                     );
    72. // Создаём таймер и устанавливаем тик на TIME_DELAY мсек
    73.  nIdTimer = ::SetTimer(hMyWnd, ID_TIMER_1, TIME_DELAY, OnTimerTick);
    74.  
    75.  ::GetClientRect(hMyWnd, &r);
    76.  HRGN hRgn1 = ::CreateRectRgnIndirect(&r);
    77.  
    78. // ::SetWindowRgn(hMyWnd, hRgn1, TRUE);
    79.  
    80.  // Here we import the function from USER32.DLL
    81.  HMODULE hUser32 = GetModuleHandle("USER32.DLL");
    82.  m_pSetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress(hUser32, "SetLayeredWindowAttributes");
    83.  
    84. // If the import did not succeed, make sure your app can handle it!
    85.  if (NULL == m_pSetLayeredWindowAttributes)
    86.     return FALSE; //Bail out!!!
    87. // Check the current state of the dialog, and then add the WS_EX_LAYERED attribute
    88.  ::SetWindowLong(hMyWnd, GWL_EXSTYLE, ::GetWindowLong(hMyWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
    89.  
    90. // Sets the window to 70% visibility.
    91.   CurrentAlpha = 120;
    92.  m_pSetLayeredWindowAttributes(hMyWnd, 0, CurrentAlpha, LWA_ALPHA);
    93.  
    94. // Подгрузили картинки, далее будем их менять
    95.  bmRU = ::LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP_RU));
    96.  bmEN = ::LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP_EN));
    97.  
    98.  // для начала зарегестрируем отдельный класс для "скрытого" родителя.
    99. // Можно и не делать этого, а воспользоваться классом главного онка!
    100.  
    101. WNDCLASSEX        WndClass;
    102.  
    103. char            szClassName[] = "HiddenWindow";
    104.  
    105. WndClass.style = CS_HREDRAW | CS_VREDRAW;
    106. WndClass.lpfnWndProc = (WNDPROC)ParentWindowProc;
    107. WndClass.cbClsExtra = 0;
    108. WndClass.cbWndExtra = 0;
    109. WndClass.hInstance = hInstance;
    110. WndClass.hIcon = 0l;
    111. WndClass.hCursor = NULL;
    112. WndClass.hbrBackground = (HBRUSH) GetStockObject (GRAY_BRUSH);
    113. WndClass.lpszMenuName = NULL;
    114. WndClass.lpszClassName = szClassName;
    115. WndClass.hIconSm = 0l;
    116.  
    117. if (!RegisterClassEx(&WndClass))
    118. {
    119.     if (!RegisterClass((LPWNDCLASS)&WndClass.style))
    120.     {
    121.         MessageBox(NULL, "Can't register the class", "Error", MB_APPLMODAL | MB_ICONSTOP);
    122.         return 0; // или "return 0;" Смотря что надо вернуть
    123.     }
    124. }
    125.  
    126. // Теперь надо создать по этому классу окно
    127.  
    128. HWND hWndParent = CreateWindow (szClassName, "Hidden Window by SUnteXx",
    129.                         WS_THICKFRAME | WS_BORDER | WS_EX_TOOLWINDOW,
    130.                         -31000,
    131.                         -31000,
    132.                         -30900,
    133.                         -30900,
    134.                         NULL,
    135.                         NULL,
    136.                         hInstance,
    137.                         NULL);
    138.  
    139. // Теперь необходимо поставить родителя нашему главному окошку
    140.  
    141. ;
    142.  LONG l = SetWindowLong(hMyWnd, GWL_HWNDPARENT, (LONG)hWndParent); // l будет равен указателю на старый родитель (или 0)!
    143.  
    144. // Если главное окно создаем после создания "скрытого" окна, то можно сразу при создании указать нашего родителя!
    145.  
    146.  
    147.  ::ShowWindow(hMyWnd, nCmdShow); // отобразить наше окно на десктопе
    148.  ::UpdateWindow(hMyWnd); // обновить клиентскую область
    149.  
    150.  while(GetMessage(&msg, NULL, 0, 0))
    151.  {
    152.   TranslateMessage(&msg);
    153.   DispatchMessage(&msg);
    154.  }
    155.  
    156.  return msg.wParam;
    157. }
    158.  
    159. // Тикаем по-тихоньку
    160. void CALLBACK OnTimerTick
    161. (
    162.     HWND hWnd,  // дескриптор CWND который вызвал SetTimer
    163.     UINT nMsg,  // WM_TIMER
    164.     UINT nIDEvent,  // идентификатор таймера
    165.     DWORD dwTime    // системное время
    166. )
    167. {
    168.  DWORD FId;
    169.  HDC hdc, hMemDC;
    170.  
    171.  hdc = ::GetDC(hWnd);
    172.  
    173. // Создаем контекст памяти, совместимый с контекстом отображения
    174.  hMemDC = ::CreateCompatibleDC(hdc);
    175.  
    176.  
    177. // Проверяем раскладку клавиатуры у активного окна и показываем
    178. // соотв-щую картинку
    179.  FId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL);
    180.  ::AttachThreadInput(::GetCurrentThreadId(), FId, TRUE);
    181.  
    182. // Send some text out into the world
    183.  switch ((DWORD)::GetKeyboardLayout(FId) & 0xFFFF) {
    184. // Раскладка английская
    185.   case 0x0409: {
    186.     //::TextOut(hdc, r.left, r.top, text_en, 2);
    187.      //::GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmEN);
    188.      ::SelectObject(hMemDC, bmEN);
    189.       break;
    190.   }
    191. // Раскладка русская
    192.   case 0x0419: {
    193. //    ::TextOut(hdc, r.left, r.top, text_ru, 2);
    194.       //::GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmRU);
    195.       ::SelectObject(hMemDC, bmRU);
    196.        break;
    197.   }
    198.  }
    199.  ::BitBlt(hdc, 0, 0, 29, 19, hMemDC, 0, 0, SRCCOPY);
    200.  // Удаляем контекст памяти
    201.  ::DeleteDC(hMemDC);
    202.  ::ReleaseDC(hWnd, hdc);
    203.  ::AttachThreadInput(::GetCurrentThreadId(), FId, FALSE);
    204. }
    205.  
    206. LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    207. {
    208.  switch(uMsg) {
    209.  
    210.  case WM_DESTROY:{
    211.   ::KillTimer(hWnd, nIdTimer);
    212.   ::DeleteObject(bmRU);  // delete bitmaps
    213.   ::DeleteObject(bmEN);
    214.   ::PostQuitMessage(NULL);
    215.   return 0;
    216.   break;
    217.  }
    218.  case WM_LBUTTONUP:{
    219.      if (::GetCapture() == hWnd) ::ReleaseCapture();
    220.      break;
    221.                             }
    222.  case WM_LBUTTONDOWN:{
    223.      SetCapture(hWnd);
    224.     point.x = LOWORD(lParam);
    225.     point.y = HIWORD(lParam);
    226.     break;
    227.                             }
    228.  case WM_MOUSEMOVE:{
    229.      if(::GetCapture() == hWnd){
    230.          if(::GetAsyncKeyState(VK_LBUTTON)){
    231.             ::GetCursorPos(&point2);
    232.             RECT wRect;
    233.             ::ScreenToClient(hWnd, &point2);
    234.             int ccx = point2.x-point.x;
    235.             int ccy = point2.y-point.y;
    236.             ::GetWindowRect(hWnd, &wRect);
    237.             ::MoveWindow(hWnd, wRect.left+ccx,
    238.                 wRect.top+ccy, wRect.right-wRect.left,
    239.                 wRect.bottom-wRect.top,TRUE);
    240.          }
    241.      }
    242.      break;
    243.                          }
    244.  case WM_MOUSEWHEEL:{
    245.   if(HIWORD(msg.wParam) <= 32512)
    246.   {
    247.       CurrentAlpha += 20;
    248.         m_pSetLayeredWindowAttributes(hMyWnd, 0, CurrentAlpha, LWA_ALPHA);
    249.   }
    250.   else
    251.   {
    252.           CurrentAlpha -= 20;
    253.  m_pSetLayeredWindowAttributes(hMyWnd, 0, CurrentAlpha, LWA_ALPHA);
    254.   };
    255.                           }
    256.  }
    257.  
    258.  return DefWindowProc(hWnd, uMsg, wParam, lParam);
    259. };
    260.  
    261. LRESULT CALLBACK ParentWindowProc (HWND hWnd, UINT uMessage, UINT wParam, LONG lParam)
    262. {
    263.     return DefWindowProc(hWnd, uMessage, wParam, lParam);
    264. }
    Как пользовать исходник. Для компиляции добавьте 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.
     
  8. uni

    uni New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    67
    Нет, не исчерпана тема. Программа почему-то не реагирует на переключение раскладки в Far'е, т.е. на консоль похоже не реагирует. Дела.
     
  9. uni

    uni New Member

    Публикаций:
    0
    Регистрация:
    23 май 2005
    Сообщения:
    67
    Ребят, может меня кто просветит? Нужно, чтобы приаттаченная тулза (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):
    1.  ::AttachThreadInput(::GetCurrentThreadId(), FId, TRUE);
    2.  newhkl = ::GetKeyboardLayout(FId);
    3.  ::AttachThreadInput(::GetCurrentThreadId(), FId, FALSE);
    Неужели так можно маскировать двойной клик, а может и ещё чего? Видимо я портачу где-то с параметрами или частотой вызова или ещё чего в данном куске кода.
    P.S4. Видимо эта строка из MSDN есть ответ на вопрос (статья про AttachThreadInput):
    Видимо я каждые энцать сотен миллисекунд просто напросто ресечу входной поток с клавы/мыши. Т.е. двойной клик видимо просто не успевает обработаться. Ёклмн, что же делать?
    P.S5. Что делать... походу эта функция аттачинга вообще не нужна. Когда выбрасываем AttachThreadInput() всё работает вроде также, но с двойным кликом. Всем спасибо. Наверное разобрался. Вот так бы всегда. Зашёл на форум и сразу разобрался, надо почаще на форумы заходить.