Programmers seem to love abstractions, blissfully unaware of what’s happening under the covers. I guess that’s part of the appeal of .NET and its endless pile of abstractions. But if you’re anything like me you need to know what’s really going on. Even if you still end up using some of those abstractions, having a sense for what lives underneath can help you debug your code and ultimately create better software.
Today I want to share just one little-known fact about Windows 8 apps and that has to do with windows. Traditionally, each desktop application on Windows had at least one top-level window created via the CreateWindow function and represented by an HWND – a window handle. It wasn’t hard to see that .NET Windows Forms apps or even WPF apps still relied on an HWND for each top-level window that appeared on the desktop.
But in this brave new Metro world where everything is so fast and fluid it seems that developers are again gloriously ignorant of the fact that each app still has an HWND. I can’t really blame them as it’s pretty well hidden, but it’s still there. A little perspective again comes in handy. If you have any experience writing Windows services you will know that you are just a guest inside your own process. The same is true of Metro apps. As soon as your application’s process is created, the clock starts ticking. If you don’t hand over the wheel to the Windows Runtime, then before long Windows will shoot you in the head and your process is gone.
It begins, at least for CRT-based applications (any app produced with Visual C++) in good-old wWinMain. Yes, the default project templates try to hide wWinMain and replaces it with a silly main function designed to look like .NET but which just wastes memory and processor cycles. Anyway, the way your app relinquishes control to the Windows Runtime is by calling the static (in the C++/CX sense) CoreApplication::Run function. Your app simply implements the IFrameworkViewSource and IFrameworkView interfaces and the Windows Runtime will let you know when it needs your attention. Of course, if you’re writing a XAML based app then this is all implemented for you – another abstraction – but it’s all still there behind the scenes.
Regardless of whether you’re writing a “CoreApplication” directly or using the XAML framework, your app will have a top-level window represented by the CoreWindow class. You cannot create a CoreWindow yourself but the Windows Runtime graciously creates one for you. Once your app is up and running (specifically any time after your IFrameworkView::SetWindow implementation is called) you can call the static CoreWindow::GetForCurrentThread function to get hold of it.
auto w = CoreWindow::GetForCurrentThread();
Now it gets a little trickier. You see, there are some things a particular Windows Runtime object would rather not share with you but might want to share with someone else. Such discrimination is provided through cloaked interfaces that are not included in the Windows Runtime metadata, and not exposed by your friendly IInspectable interface. You have to use good old QueryInterface to discover whether such a cloaked interface is being offered. Of course, you first need to know what to query for. I’ll save you the trouble of guessing. Here it is:
struct __declspec(uuid("45D64A29-A63E-4CB6-B498-5781D298CB4F")) __declspec(novtable)
ICoreWindowInterop : IUnknown
virtual HRESULT __stdcall get_WindowHandle(HWND * hwnd) = 0;
virtual HRESULT __stdcall put_MessageHandled(unsigned char) = 0;
You can now crack open your CoreWindow with reinterpret_cast to reveal IUnknown and then query for the ICoreWindowInterop interface:
Now it’s a simple matter of calling the get_WindowHandle virtual function to get your app’s HWND.
Don’t believe me? Just declare your old friend GetWindowText from USER32 and you will be rewarded with the window’s title, which the Windows Runtime sets based on information in your app’s package manifest.
int __stdcall GetWindowTextW(HWND hwnd, PWSTR text, int count);
GetWindowTextW(hwnd, text, _countof(text));
So what can you do with this HWND? Well not very much. The reality is that everything on Windows 8 is rendered with Direct3D. Even your aging GDI app is ultimately composited together with other windows and presented to the screen as if the whole desktop were one giant DirectX application, and in some ways that’s what it is.
So even though you can get the HWND for your app, you already have access to the window’s Direct3D surface which is far more powerful than anything you could do with the HWND on your own. So why does this cloaked interface exist? Why to allow Direct3D to get your HWND of course! Oh, and ICoreWindowInterop exists on Windows Phone 8 as well.