{"id":111971,"date":"2026-01-07T07:00:00","date_gmt":"2026-01-07T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111971"},"modified":"2026-01-07T07:20:45","modified_gmt":"2026-01-07T15:20:45","slug":"20260107-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260107-00\/?p=111971","title":{"rendered":"How can I find out where the Windows caret is?"},"content":{"rendered":"<p>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 <code>Get\u00adCaret\u00adPos<\/code>, but it always failed.<\/p>\n<p>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 <code>Get\u00adCaret\u00adPos<\/code> 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 <code>Attach\u00adThread\u00adInput<\/code>&#8216;d to, either explicitly or implicitly.)\u00b9<\/p>\n<p>To get the global state, you can call <code>Get\u00adGUI\u00adThread\u00adInfo<\/code> with a thread ID of zero to say that you want the information of whatever thread owns the foreground window.<\/p>\n<pre>GUITHREADINFO info = { sizeof(info) };\r\nif (GetGUIThreadInfo(0, &amp;info)) {\r\n    if (info.flags &amp; GUI_CARETBLINKING) {\r\n        \u27e6 info.rcCaret contains the location of the caret \u27e7\r\n        \u27e6 relative to info.hwndCaret                      \u27e7\r\n    }\r\n}\r\n<\/pre>\n<p>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:<\/p>\n<pre>GUITHREADINFO info = { sizeof(info) };\r\nif (GetGUIThreadInfo(0, &amp;info)) {\r\n    if (info.flags &amp; GUI_CARETBLINKING) {\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Convert rcCaret to screen coordinates                           <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">MapWindowPoints(info.hwndCaret, nullptr, (POINT*)&amp;info.rcCaret, 2);<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                                  <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ Move the cursor to the bottom right corner                      <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">SetCursorPos(info.rcCaret.right - 1, info.rcCaret.bottom - 1);     <\/span>\r\n    }\r\n}\r\n<\/pre>\n<p>But there are times when the <code>GUI_<wbr \/>CARET\u00adBLINKING<\/code> 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 <code>Create\u00adCaret<\/code> but are instead drawing a custom caret that blinks on a custom timer.<\/p>\n<p>We&#8217;ll look at that next time.<\/p>\n<p>\u00b9 Things that are local to the current thread (and any other threads it is attached to) include<\/p>\n<ul>\n<li>The capture, focus, and active windows,<\/li>\n<li>The input queue and message queue,<\/li>\n<li>The mouse cursor shape and show count,<\/li>\n<li>The keyboard state,<\/li>\n<li>The caret.<\/li>\n<\/ul>\n<p>In Windows 95, these things were kept in a structure called the &#8220;virtual window information&#8221; 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 &#8220;vwi&#8221;, which was pronounced &#8220;vee-wee&#8221;. So you might overhear people on the window manager team saying something like &#8220;You can&#8217;t capture to a window that belongs to somebody else&#8217;s vee-wee.&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You&#8217;ll have go to a larger scope.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111971","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You&#8217;ll have go to a larger scope.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111971","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111971"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111971\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111971"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111971"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}