Hello, Habr. Today I want to present for your consideration a small utility-utility, which will show the keyboard layout indicator next to the mouse text cursor.

Intro
A long time ago (it seems, last Friday), about three years ago, there was a kind of free program (I’ll not give the name, the author didn’t pay me for this year :)). She is still there, only worth the money. All its functionality consisted in showing the current keyboard layout next to the mouse
text cursor. The program was sitting in the tray, constantly displaying some pop-ups, (it seems) bursting into the Internet, getting underfoot, faithfully looking into the eyes, etc. In general, a restless patient. I saw her, admired the idea and tried to use it in my daily work. It seemed to me very uncomfortable for the reasons described above, and then I decided to write my own, with blackjack and ... hmm, without popups. Having looked a bit (a lot is impossible, in the EULA it is written so) the algorithm of its work, I found out that only a few API calls are used to achieve the desired effect.
Cursor
So, for a start, let's figure out how to change the text cursor icon. MSDN tells us that this can be done by calling SetSystemCursor. The first parameter is to pass the handle to the cursor, and the second to indicate which cursor we are changing - the main one, the busy cursor, the text cursor, etc. In this particular case, the second parameter will be OCR_IBEAM, and we will now deal with the first one. After carefully reading the article on SetSystemCursor (which I didn’t do for the first time), you can see that you cannot transfer a handle that you received using the LoadCursor function to the cursor, because the system destroys the cursor passed to SetSystemCursor (commendable cleanliness, otherwise in other places). It also says in MSDN that this problem is solved by copying the cursor before passing it to SetSystemCursor using the CopyCursor function. So, if we add a resource with a cursor to exe-schnick, you can use it like this:
')
HCURSOR hRuCur = LoadCursorA(GetModuleHandleA(0), MAKEINTRESOURCEA(IDC_RUS));<br>HCURSOR hRuCopy = CopyCursor(hRuCur);<br>SetSystemCursor(hRuCopy, OCR_IBEAM); <br><br> * This source code was highlighted with Source Code Highlighter .
This code will replace the text mouse cursor (I-beam) with the cursor loaded from the IDC_RUS resource. And it will replace in all applications.
Layout
How to change the cursor, we found out. Now let's look at how to determine the keyboard layout for the window, over which the mouse cursor is now located. Let's go from the end - define the window above the cursor. This is done using the following sequence of API calls: call GetCursorPos to determine the current coordinates of the cursor, then call WindowFromPoint with the received coordinates. Voila, we've got the handle. Why do we need it? :) To define the keyboard layout in the window under the cursor. How to do it? Call GetKeyboardLayout, of course! Only here it needs some obscure thread id - the thread id in which the window was created. A little digging in the adjacent sections of MSDN, you can find the function GetWindowThreadProcessId, which returns the id of the thread that created it using the known window handle. Puzzle gathered:
POINT p;<br>HWND wnd;<br>HKL lay;<br>DWORD dwThreadId;<br>GetCursorPos(&p);<br>wnd = WindowFromPoint(p);<br>dwThreadId = GetWindowThreadProcessId(wnd, 0);<br>lay = GetKeyboardLayout(dwThreadId); <br><br> * This source code was highlighted with Source Code Highlighter .
The codes of keyboard layouts can be found in MSDN, if you search for a long time :) And you can put a breakpoint on the last line and look at the layouts for different layouts.
Together
Now we know all the parts of our future utility, we can put them together. Because My main motivation was to create a light and very small program, I put all the above code right in WinMain and made it spin there indefinitely without the possibility of exiting the loop. And why, exactly? Full listing looks like this:
int __stdcall WinMain(__in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )<br>{<br> HCURSOR hRuCur = LoadCursorA(GetModuleHandleA(0), MAKEINTRESOURCEA(IDC_RUS));<br> HCURSOR hEnCur = LoadCursorA(GetModuleHandleA(0), MAKEINTRESOURCEA(IDC_ENG));<br> if (!hEnCur || !hRuCur)<br> {<br> MessageBoxA(0, "Failed to load cursors. Terminating." , "Fatal error" , MB_ICONERROR);<br> return 0;<br> }<br><br> while (1)<br> {<br> HCURSOR hRuCopy;<br> HCURSOR hEnCopy;<br> HKL lay = 0;<br> POINT p;<br> HWND wnd;<br> DWORD dwThreadId = 0;<br><br> hRuCopy = CopyCursor(hRuCur);<br> hEnCopy = CopyCursor(hEnCur);<br><br> GetCursorPos(&p);<br> wnd = WindowFromPoint(p);<br> if (!IsWindow(wnd)) continue ;<br><br> dwThreadId = GetWindowThreadProcessId(wnd, 0);<br> lay = GetKeyboardLayout(dwThreadId);<br><br> if ((DWORD)lay == 0x4190419 /*RU*/ ) SetSystemCursor(hRuCopy, OCR_IBEAM);<br> if ((DWORD)lay == 0x4090409 /*EN*/ ) SetSystemCursor(hEnCopy, OCR_IBEAM);<br><br> Sleep(250);<br> }<br><br> return 0;<br>} <br><br> * This source code was highlighted with Source Code Highlighter .
Project in VS
To create a project in visual studio, you need to select Win32 - Win32 Project - Empty project, add a cpp-file with the above-described winmain function and add two options for cursors - for systems with two layouts, naturally :) Cursors can be taken ready in the internet, or you can draw it yourself right in the studio. But that is not all! After all, it was about a “small utility-utility”, right? A monster in 70Kb does not fit this description :) Therefore, we climb under the hood and remove all that is possible. First, the project properties - Linker - Advanced - Entry point - WinMain. Secondly, the project properties - Linker - Command line - align: 16. Thirdly, you can still poke the buttons to your taste. After recompilation, I got 3Kb, of which 1.5Kb are cursors. Maybe even less, I don't know.
Autro
The code can be called “dirty”, ugly, etc. However, it performs its function for one hundred percent - the mouse cursor has now become more informative, no one throws out a crowd of unnecessary popups in the system tray, and the priceless RAM is free to accommodate the Windows Help and Support service absolutely necessary for it.