Using C++/CX in Desktop apps

September 29, 2012

In my first and second post on using WinRT in a desktop app, we’ve used the raw API and then WRL to create and access WinRT objects. It would be easier to access WinRT using the new C++/CX extensions. Can we do that from a desktop app? Let’s give it a try.

We’ll start with a regular Win32 Console application project. The first thing we need to do is to enable the C++/CX extensions. Open project properties and navigate to the C/C++ / General node and set “Consume Windows Runtime Extension” to Yes:

SNAGHTMLb26f7df

Building the project now causes the compiler to complain that a certain setting (minimal rebuild) is incompatible with C++/CX, so we have to disable it. Open project properties again and navigate to C/C++ / Code Generation and disable minimal rebuild:

SNAGHTMLb274064

Building the project now still fails with an error that says: “could not find assembly ‘platform.winmd’: please specify the assembly search path using /AI or by setting the LIBPATH environment variable”. Clearly, it wants the platform.winmd metadata file. We can find it in the directory like this: C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcpackages. Trying to use this folder with a #using statement (with platform.winmd as suffix) has no effect (for some reason). The way to fix this is to go to project properties again, find the C/C++ / General node and add the folder to the “Additional #using Directories” value:

SNAGHTMLb3561c9 

Building the project now fails again, for the same reason, now looking for another file, windows.winmd (that references the entire WinRT API). We need to add the folder this one is found (C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral) to the same value after a semicolon.

Now the project finally builds successfully, but the compiler issues a warning, suggesting we replace the classic main() function with a one that uses WinRT types. Let’s do that:

using namespace Platform;

int main(Array<String^>^ args) {

All that’s left to do now is use C++/CX as usual. Here’s an example with the (now classic) Calendar:

#include <iostream>

using namespace std;
using namespace Windows::Globalization;
using namespace Platform;

int main(Array<String^>^ args) {
    auto calendar = ref new Calendar;
    calendar->SetToNow();
    wcout << "It's now " << calendar->HourAsPaddedString(2)->Data() << L":" << 
        calendar->MinuteAsPaddedString(2)->Data() << L":" <<
        calendar->SecondAsPaddedString(2)->Data() << endl;
    return 0;
}

Astute readers may wonder where is the WinRT initialization (RoInitialize)? Apparently, this happens automatically when we switch to a WinRT-based main. Switching back to the classic main() throws an exception at runtime, as WinRT is not initialized. We can call RoInitialize() explictly to mitigate that.

In the above code, what apartment is selected for the current thread? We can check that with an API introduced back in Windows 7 (for COM) that allows querying the current apartment the thread is in, CoGetApartmentType:

    APTTYPE at;
    APTTYPEQUALIFIER atq;
    ::CoGetApartmentType(&at, &atq);

Looking at the result, it’s the multithreaded apartment.

So, there you have it. WinRT from a desktop app. In fact, in the recent documentation refresh, WinRT types are specifically documented as being able or unable to run in a desktop app. Calendar is one that can. And a lot others can.

The more I work with WinRT, the more I feel WinRT is a better COM – it’s the way COM should have been back in the day. Better late then never!

Some things are missing, though. When creating a WinRT Component DLL, it does not implement DllRegisterServer, so we can’t automatically register it (via RegSvr32.exe) for desktop app use. For a Store app, that’s not a problem, because the App manifest indicates external dependencies, but desktop apps have no such facility. If it did, we were almost in COM heaven.

Source C++/CX Demo

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

7 comments

  1. Deon BrewisOctober 5, 2012 ב 01:36

    Great article!

    It’s very sad that we did not ship any Visual Studio templates that sets this up for you, but there is one available now that you can download from the “Extensions and Updates” in Visual Studio – search the online gallery for “CxxWinRTTemplate”. Thanks Sridhar!

    One comment – in addition to the main format with the (Array) arguments, you can also use a traditional int main(…) and adorn it with the [Platform::MTAThread] attribute.

    The advantage of doing either of these rather than calling RoInitialize explicitly in main() is that the CRT will ensure that WinRT is initialized BEFORE running any user-defined global constructors, and uninitialized AFTER any user-defined global destructors.

    This allows you to do a global:
    SomeRefClass^ g_r = ref new SomeRefClass();

    This is really only for EXE’s. The traditional DLLMain issues remain in WinRT unfortunately :(.

    – Deon Brewis
    Microsoft Visual C++ Development Team

    Reply
  2. pavelyOctober 5, 2012 ב 06:49

    Thank you for that, Deon!

    Reply
  3. Pooya EimandarOctober 28, 2012 ב 16:57

    Great article.
    Could we publish this app on windows 7 or vista?Does it work?

    Thanks alot.

    Reply
  4. pavelyOctober 28, 2012 ב 19:40

    Nope. This only works on Windows 8.

    Reply
  5. Steven LissJune 22, 2015 ב 21:37

    I’d like to add one note that I had some trouble with. You may get errors akin to “Platform::Object::Object()” could not be imported because it was already defined. You may see this on an array of type definitions. This will be accompanied by error code, c2535.

    You may also see, “use of this type requires a reference to assembly ‘mscorlib'”. Accompanied by error code C3624.

    If you see these errors, you’re Visual Studio tools are incompatible with the WinRT components you are targeting. I also believe this can arise from a bug in early versions of VS2012. Ensure your version of Visual Studio is compatible with the version of Windows you are targeting. If it is, ensure you’re version of VS is up to date. Or you’ll spend two days chasing phantoms, like I just did.

    Reply
  6. Steven LissMarch 4, 2016 ב 23:09

    Just as a note, it is better to use Visual Studio macros to specify the paths to the metadata directories.

    For platform.winmd, if you use $(VCInstallDir)vcpackages, then if your solution changes Visual Studio version the reference won’t break.

    Similarly for windows.winmd, you can use $(WindowsSDKDir)UnionMetadata

    Reply
  7. Seva AlekseyevDecember 17, 2016 ב 18:53

    Thanks! Works as expected for Windows 10 UWPlskseyev as well. I’ve taken the liberty to republish the recipe here: http://stackoverflow.com/a/41192702/219159

    Reply