{"id":97116,"date":"2017-10-02T07:00:00","date_gmt":"2017-10-02T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=97116"},"modified":"2019-03-13T01:17:42","modified_gmt":"2019-03-13T08:17:42","slug":"20171002-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20171002-00\/?p=97116","title":{"rendered":"How can I detect that my window is on the current virtual desktop?"},"content":{"rendered":"<p>Virtual desktops are a feature added in Windows 10 blah blah exposition. <\/p>\n<p>Here is how virtual desktops work, from a programmatic standpoint: <\/p>\n<ul>\n<li>    To switch to a virtual desktop, the system shows the windows     that belong to the virtual desktop and hides the windows that     do not belong to the virtual desktop. Note that the windows     still all belong to the same desktop (hence the &#8220;virtual&#8221;).     All we&#8217;re doing is hiding and showing windows. <\/li>\n<li>    When a new window is shown, it gets placed on the current     virtual desktop. <\/li>\n<li>    When a window becomes foreground, the system switches to the     virtual desktop that the window belongs to. <\/li>\n<\/ul>\n<p>That said, there are some guidelines that programs should follow. <\/p>\n<ul>\n<li>    Do not programatically change the current virtual desktop.     The user should be the one to change virtual desktops,     if that&#8217;s what they want. <\/li>\n<li>    If your program decides to open a new window,     then open a new window.     It will be placed on the current virtual desktop. <\/li>\n<li>    If your program decides to reuse an existing window     (for example, if you have a tabbed user interface,     and you want to open the document in a new tab),     then when looking for a window to reuse,     limit your search to the current virtual desktop.     If you cannot find a window from the current virtual desktop,     then create and show a new one,     which will be placed on the current virtual desktop. <\/li>\n<li>    Exception:     If your program opens each document in a new window,     and the user opens a document that you already have a window for,     then you are allowed to switch to the virtual desktop that contains     the already-open document. <\/li>\n<\/ul>\n<p>Let&#8217;s start with <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\">the scratch program<\/a> and make these changes. <\/p>\n<pre>\n<font COLOR=\"blue\">#include &lt;shlobj.h&gt;<\/font>\n\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n    <font COLOR=\"blue\">g_hwndChild = CreateWindow(\"listbox\", nullptr,\n       WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, hwnd,\n       (HMENU)IntToPtr(1), g_hinst, 0);<\/font>\n\n    return TRUE;\n}\n\n<font COLOR=\"blue\">void\nProcessCommandLine(LPCSTR pszMessage)\n{\n    ListBox_AddString(g_hwndChild, pszMessage);\n}\n\nvoid\nOnCopyData(HWND hwnd, HWND hwndFrom, PCOPYDATASTRUCT pcds)\n{\n    if (pcds-&gt;dwData == 0)\n    {\n      \/\/ WARNING! Parameter validation is missing!\n      ProcessCommandLine(reinterpret_cast&lt;PSTR&gt;(pcds-&gt;lpData));\n    }\n}\n\n    \/\/ Add to WndProc\n    HANDLE_MSG(hwnd, WM_COPYDATA, OnCopyData);\n\nBOOL\nWindowCanBeReused(HWND hwnd)\n{\n    \/\/ A more realistic program would have some evaluation criteria.\n    return TRUE;\n}\n\nBOOL\nTryHandOffToExistingInstance(LPCSTR pszMessage)\n{\n    HWND hwndFound = nullptr;\n    while ((hwndFound = FindWindowEx(nullptr, hwndFound,\n                        \"Scratch\", nullptr)) != nullptr) {\n      if (WindowCanBeReused(hwndFound)) {\n        SetForegroundWindow(hwndFound);\n        COPYDATASTRUCT cds;\n        cds.dwData = 0;\n        cds.cbData = lstrlen(pszMessage) + 1;\n        cds.lpData = const_cast&lt;PSTR&gt;(pszMessage);\n        FORWARD_WM_COPYDATA(hwndFound, nullptr, &amp;cds, SendMessage);\n        return TRUE;\n      }\n    }\n    return FALSE;\n}\n<\/font>\n\nint WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\n                   LPSTR lpCmdLine, int nShowCmd)\n{\n    MSG msg;\n    HWND hwnd;\n\n    g_hinst = hinst;\n\n    if (!InitApp()) return 0;\n\n    if (SUCCEEDED(CoInitialize(NULL))) {\/* In case we use COM *\/\n\n      <font COLOR=\"blue\">if (!lpCmdLine[0]) {\n        lpCmdLine = const_cast&lt;PSTR&gt;(\"(empty command line)\");\n      }\n      \n      if (!TryHandOffToExistingInstance(lpCmdLine)) {<\/font>\n\n        hwnd = CreateWindow(\n            TEXT(\"Scratch\"),                \/* Class Name *\/\n            TEXT(\"Scratch\"),                \/* Title *\/\n            WS_OVERLAPPEDWINDOW,            \/* Style *\/\n            CW_USEDEFAULT, CW_USEDEFAULT,   \/* Position *\/\n            CW_USEDEFAULT, CW_USEDEFAULT,   \/* Size *\/\n            NULL,                           \/* Parent *\/\n            NULL,                           \/* No menu *\/\n            hinst,                          \/* Instance *\/\n            0);                             \/* No special parameters *\/\n\n        ShowWindow(hwnd, nShowCmd);\n\n        while (GetMessage(&amp;msg, NULL, 0, 0)) {\n            TranslateMessage(&amp;msg);\n            DispatchMessage(&amp;msg);\n        }\n      <font COLOR=\"blue\">}<\/font>\n\n      CoUninitialize();\n    }\n\n    return 0;\n}\n<\/pre>\n<p>This is our non-virtual-desktop-aware version of the program. When it is run, it looks for an existing instance that can be reused, and if it finds one, it asks that existing instance to handle the command line. <\/p>\n<p>Now let&#8217;s make this program virtual-desktop-aware. <\/p>\n<pre>\n<font COLOR=\"blue\">IVirtualDesktopManager* g_pvdm;<\/font>\n\nBOOL\nWindowCanBeReused(HWND hwnd)\n{\n    <font COLOR=\"blue\">BOOL isCurrent;\n    if (g_pvdm &amp;&amp;\n        SUCCEEDED(g_pvdm-&gt;IsWindowOnCurrentVirtualDesktop(hwnd,\n                                          &amp;isCurrent)) &amp;&amp;\n        !isCurrent) {\n      return FALSE;\n    }<\/font>\n\n    \/\/ A more realistic program would have some evaluation criteria.\n    return TRUE;\n}\n\nint WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\n                   LPSTR lpCmdLine, int nShowCmd)\n{\n    ...\n    \n    if (SUCCEEDED(CoInitialize(NULL))) {\/* In case we use COM *\/\n\n      <font COLOR=\"blue\">\/\/ This can fail if the system does not support virtual desktops.\n      CoCreateInstance(CLSID_VirtualDesktopManager,\n                       nullptr, CLSCTX_ALL, IID_PPV_ARGS(&amp;g_pvdm));<\/font>\n\n      if (!lpCmdLine[0]) {\n        lpCmdLine = const_cast&lt;PSTR&gt;(\"(empty command line)\");\n      }\n      \n      if (!TryHandOffToExistingInstance(lpCmdLine)) {\n         ...\n      }\n\n      <font COLOR=\"blue\">if (g_pvdm) g_pvdm-&gt;Release();<\/font>\n\n      CoUninitialize();\n    }\n\n    return 0;\n}\n<\/pre>\n<p>We updated the <code>Window&shy;Can&shy;Be&shy;Reused<\/code> function so it takes the virtual desktop state into account. Specifically, we will not attempt to reuse windows that are not part of the current virtual desktop. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>And what are the recommended policies for using virtual desktops anyway?<\/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-97116","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>And what are the recommended policies for using virtual desktops anyway?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/97116","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=97116"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/97116\/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=97116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=97116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=97116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}