March 21st, 2024

Using the Display­Information class from a desktop Win32 application, part 2

Last time, we tried to create a Display­Information from an HWND, but it failed with the message “A DispatcherQueue is required for DisplayInformation created from an HWND.”

So I guess we need to make sure the thread has a Dispatcher­Queue.

#include <DispatcherQueue.h>
#include <windows.graphics.display.interop.h>
#include <winrt/Windows.Graphics.Display.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>    

namespace winrt
{
    using namespace winrt::Windows::Graphics::Display;
    using namespace winrt::Windows::System;
}

namespace ABI                            
{                                        
    using namespace ABI::Windows::System;
}                                        
winrt::DispatcherQueueController g_controller{ nullptr };
winrt::DisplayInformation g_info{ nullptr };

OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) noexcept
{
    DispatcherQueueOptions options{                              
        sizeof(options), DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE };
    winrt::check_hresult(                                        
        CreateDispatcherQueueController(options,                 
            reinterpret_cast<ABI::IDispatcherQueueController**>( 
                controller.put())));                             

    g_info = wil::capture_interop<winrt::DisplayInformation>
        (&IDisplayInformationStaticsInterop::GetForWindow, hwnd);

    return TRUE;
}
catch (...)
{
    return FALSE;
}

winrt::fire_and_forget
OnDestroy(HWND hwnd)
{
    g_info = nullptr;
    co_await g_controller.ShutdownQueueAsync();
    PostQuitMessage(0);
}

We use Create­Dispatcher­Queue­Controller to attach a dispatcher queue to our existing message pump. There is a bit of a hassle with the second parameter because we are mixing the C++/WinRT and Win32 ABI versions of the IDispatcher­Queue­Controller interfaces. (Previous discussion.) And since we created the dispatcher queue controller, we also have to shut down the dispatcher queue when we’re done, which we take care of in OnDestroy().

Hooray, this code now successfully creates a Dispatcher­Queue­Controller that manages a Dispatcher­Queue on the current thread, and that removes the obstacle that was preventing Get­For­Window from succeeding.

Now we can hook up the event and start reading the orientation.

OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) noexcept
{
    DispatcherQueueOptions options{
        sizeof(options), DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE };
    winrt::check_hresult(
        CreateDispatcherQueueController(options,
            reinterpret_cast<ABI::IDispatcherQueueController**>(
                controller.put())));

    g_info.OrientationChanged([hwnd](auto&&, auto&&)              
        {                                                         
            InvalidateRect(hwnd, nullptr, TRUE);                  
        });                                                       

    g_info = wil::capture_interop<winrt::DisplayInformation>
        (&IDisplayInformationStaticsInterop::GetForWindow, hwnd);

    return TRUE;
}
catch (...)
{
    return FALSE;
}

void
PaintContent(HWND hwnd, PAINTSTRUCT* pps) noexcept
{
    PCWSTR message;                                   
    switch (g_info.CurrentOrientation()) {            
    case winrt::DisplayOrientations::Landscape:       
        message = L"Landscape"; break;                
    case winrt::DisplayOrientations::Portrait:        
        message = L"Portrait"; break;                 
    case winrt::DisplayOrientations::LandscapeFlipped:
        message = L"LandscapeFlipped"; break;         
    case winrt::DisplayOrientations::PortraitFlipped: 
        message = L"PortraitFlipped"; break;          
    default:                                          
        message = L"Unknown"; break;                  
    }                                                 
    TextOut(pps->hdc, 0, 0, message,                  
        static_cast<int>(wcslen(message)));           
}

After creating the Display­Information, we register for the orientation change event and force a repaint when that happens.

Our paint handler simply prints the current orientation of the monitor that the window is on.

I won’t bother demonstrating it here, but you can also use Get­For­Monitor to get a Display­Information for a specific monitor.

 
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.

13 comments

Discussion is closed. Login to edit/delete existing comments.

  • GL

    If I’m just tracking the monitor of the window, can I achieve it by listening for WM_DeviceChange, WM_DisplayChange, WM_WindowPosChanging, WM_WindowPosChanged?

    • Raymond ChenMicrosoft employee Author

      Sure. But this question was about detecting display orientation. You can try to infer portrait/landscape by looking at the dimensions, but that won’t distinguish between portrait and portrait-flipped, for example. (dmOrientation works only for printers.)

      • Кирилл Ярин

        So, what is the best way to have a desktop application window with client area and some controls in it staying in the same physical place as screen rotates, while icons/text on these controls would follow the screen orientation?

      • GL

        Most of the time, “nothing”. A screen rotation is a special case of resolution changes, so most apps and Windows already keep themselves/them at sane locations. Also, most apps only have to care about its window size, not the screen size/orientation.

      • GL

        I think the fields are dmPelsWidth, dmPelsHeight, dmDisplayOrientation. According to the docs of Windows.Graphics.Display.DisplayOrientations and DEVMODEW, the result of GetcurrentOrientation should be consistent with the following:

        <code>

        Read more
    • Paulo Pinto

      As things turned out, it is quite safe to ignore anything related to UWP and stick to plain Win32.

      • alan robinson

        I don’t think any of those tell you the orientation, though? if they do, jeeze that would be easier than the nonsense in today’ s post.

      • Paulo Pinto

        You make use of ChangeDisplaySettingsEx () and EnumDisplaySettingsEx() family of functions, DEVMODE structure.

        The union field dmDisplayOrientation has what you’re looking for.

      • Joe Beans

        That has been my strategy, just for the sheer sanity. Whenever I see new UWP functionality my first thought experiment is: “What Win32 APIs are they using for that?”

  • Sigge Mannen

    What’s the reason that DisplayInformation needs a Queue? Is DispatcherQueue some sort of equavalent of message pump and DisplayInformation *thing* uses it to be able to serialize some calls across the threads, because DisplayInformation does its work on some ThreadPool and not on STA? Or am i confusing myself

  • Yuri Khan

    I notice you talk about “the” monitor the window is on as if there is one and only one. The DisplayInformation documentation, too, mentions “if your app is moved from one monitor to another monitor” as if that’s an unambiguous atomic event such that one moment the window is on one monitor and the next moment suddenly on another.

    • Chris Iverson

      You read the docs, and missed the line "A DisplayInformation instance does not map to a specific display, but instead tracks display-related information for wherever the application view is placed."

      My understanding is, it doesn't try to deal with monitor info, especially multimonitor info if your window is displayed across multiple monitors.

      It only deals with the properties that are applied to the window at that time. And only one monitor's properties can be applied to the...

      Read more