January 7th, 2026
intriguing1 reaction

How can I find out where the Windows caret is?

A customer was looking for a way to find the location of the caret (the blinking line that indicates where the next character will be inserted). They tried Get­Caret­Pos, but it always failed.

Most window manager state functions that were global in 16-bit Windows became per-thread in 32-bit Windows, as part of the conversion to the asynchronous input model. The Get­Caret­Pos function returns the caret position for your thread. (Specifically, the caret that belongs to the current thread and shared with all the other threads that the current thread has been Attach­Thread­Input‘d to, either explicitly or implicitly.)¹

To get the global state, you can call Get­GUI­Thread­Info with a thread ID of zero to say that you want the information of whatever thread owns the foreground window.

GUITHREADINFO info = { sizeof(info) };
if (GetGUIThreadInfo(0, &info)) {
    if (info.flags & GUI_CARETBLINKING) {
        ⟦ info.rcCaret contains the location of the caret ⟧
        ⟦ relative to info.hwndCaret                      ⟧
    }
}

The customer explained that they were writing an accessibility tool that moves the mouse to wherever keyboard focus is. So they filled in the code like this:

GUITHREADINFO info = { sizeof(info) };
if (GetGUIThreadInfo(0, &info)) {
    if (info.flags & GUI_CARETBLINKING) {
        // Convert rcCaret to screen coordinates                           
        MapWindowPoints(info.hwndCaret, nullptr, (POINT*)&info.rcCaret, 2);
                                                                           
        // Move the cursor to the bottom right corner                      
        SetCursorPos(info.rcCaret.right - 1, info.rcCaret.bottom - 1);     
    }
}

But there are times when the GUI_CARET­BLINKING flag is not set, even though you can see a blinking caret with your own eyes. These are cases where the program with keyboard focus is not using Create­Caret but are instead drawing a custom caret that blinks on a custom timer.

We’ll look at that next time.

¹ Things that are local to the current thread (and any other threads it is attached to) include

  • The capture, focus, and active windows,
  • The input queue and message queue,
  • The mouse cursor shape and show count,
  • The keyboard state,
  • The caret.

In Windows 95, these things were kept in a structure called the “virtual window information” because it was taking what used to be global state in Windows 3.1 and making it local state, virtualizing each thread into thinking that it was controlling the show. The abbreviation for the virtual information was “vwi”, which was pronounced “vee-wee”. So you might overhear people on the window manager team saying something like “You can’t capture to a window that belongs to somebody else’s vee-wee.”

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

1 comment

Sort by :
  • LB 7 seconds ago

    I’m guessing the solution will be related to IME, like how the emoji picker knows where to appear