«

»

Mar
20
2012

How to Consume DLLs in a WinRT/Metro Project

Yesterday, I published a small guide on how to consume DLLs in Visual C++ that explained how to best integrate a third-party library into a Visual C++ project. This is a follow-up article for Visual Studio 11 Beta users that contains the additional steps required to consume a normal (non WinRT Component) DLL in a WinRT/Metro project.

Assuming you have the DLL integrated into a WinRT application project as explained in my previous blog post, you will notice that when you try to run the application, there will be an error message stating that the Windows Metro style application could not be activated and the output window will report a missing dependency:

Screenshot of Visual Studio 11 reporting an activation error on a WinRT/Metro application

The reason for the error is that unlike desktop applications, a WinRT/Metro application is "deployed": packaged, sent to your installed app repository and remotely launched, not unlike what happens when you write a Windows Phone or Xbox application. The library DLL will simply not be part of the deployed files, even though it is in the project’s output directory.

Adding the DLL to Your Project

To solve this, you have to make the compiled DLL a part of the project. This creates a problem, because the compiled DLL will be in different directories, depending on whether you are compiling a Debug or a Release build and whether you are building for x86 or x64. Luckily, Visual C++ project files are based on MSBuild, an XML-based build scripting language.

Here’s what I did: first, I changed the output directories of my DLL project and my application to sane (and identical) values since the defaults appear rather arbitrary to me:

Screenshot showing where to find the output directory setting in a Visual Studio 11 project

Using the $(Configuration) variable, the properties can be identical for the Debug and Release configurations, but I still needed to enter them 3 times, once for each target platform supported by my project:

PlatformOutput DirectoryIntermediate Directory
Win32bin\x86\$(Configuration)obj\x86\$(Configuration)
x64bin\x64\$(Configuration)obj\x64\$(Configuration)
ARMbin\arm\$(Configuration)obj\arm\$(Configuration)

Next, I did a single build (any configuration is okay) and added the created DLL from my library project to my application project:

Screenshot of the context menu used to add existing items to a project in Visual Studio 11

I then closed Visual Studio and edited the .vcxproj file of my application project via Notepad to use MSBuild’s OutDir variable instead of the fixed path:

<ItemGroup>
  <CustomBuild Include="..\Library\$(OutDir)Library.dll">
  </CustomBuild>
</ItemGroup>

Yep, that works perfectly and Visual Studio also handles it nicely.

Deploying the DLL with Your Project

The next step is to configure the DLL to be deployed together with your project. This is rather easy: right-click the DLL, select "Properties" and set the file’s "Content" property to true:

Screenshot of the deployable content property for a file in a Visual Studio 11 project

If you’ve done everything as described, pressing F5 should now successfully execute the application and its library!

Optional: Remove .ref File

I could stop here, but now I have both a .ref and a .dll file for the library in my project. I can as well attach the custom build steps (see previous blog post) to the .dll file instead and remove the .ref

Here’s an example solution with containing an application and a DLL consumed by the application:

Possible Errors

If you are overly zealous in cleaning up build artifacts from your projects (I am!), you might run into the following error message:

Error    1    Error : DEP0600 : The following unexpected error occurred during deployment:
Illegal characters in path.
at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
at System.IO.Path.Combine(String path1, String path2)
at Microsoft.VisualStudio.ImmersiveProjectServices.Shared.AppxLayoutManager.CheckPackageLayoutState(DeployPackageName deployPackageName, String location)
at Microsoft.VisualStudio.ImmersiveProjectServices.Shared.LocalDeployJob.GetLayoutState(DeployPackageName deployName, Boolean hasFrameworkDependencies)
at Microsoft.VisualStudio.ImmersiveProjectServices.Shared.RegisterAppxLayout.Start(Boolean forceNewLayout, Boolean forceRegistration, NetworkLoopbackState desiredNetworkLoopbackState, Boolean refreshLayoutOnly, String& packageMoniker, String& firstUserAppID, Exception& deployException)

If this happens, it means that your application is already registered in the WinRT/Metro app repository, but Visual Studio has lost the registration information (or something at least) which was stored in one of those intermediate directories.

To solve it, press Windows+Q (shows all installed applications), right-click on your application and pick "uninstall". After that, deployment should succeed again.

If the error persists, open your app’s Package.appxmanifest, navigate to the "Packaging" tab and enter a new Guid for the "Package Name". That way, your project gets a new identity and whatever is left in the WinRT/Metro app repository has no connection (and therefore won’t block) your app anymore.

5 comments

  1. Steve Hazen says:

    Hi Cygon, nice blog.
    I noticed you pulled your post about c++/cx classes not allowing public members with native code in them (I read it in my rss feed but the link to it no longer works). What made you decide to pull your post? I think it is a legitimate mini rant about the design paradigm shift that we’ll have to adjust to. Are you planning to continue using iso classes and only use the RT types when crossing the ABI boundary, but even that represents a shift in design that folks are going to feel resistant to at first.

  2. Cygon says:

    Well, about 30 minutes after I had written my rant, I noticed that I can use the internal keyword in ref classes, solving most of my issues. I felt mighty stupid and deleted my post, hoping nobody would notice :p

    I’m now combining internal, the Platform::Agile smart pointer and the pImpl idiom to completely hide away any WinRT code behind clean ISO C++ classes.

    This works out so well that I’m definitely going to continue wrapping any OS-specific API behind a neutral interface. This, for example, is a Direct3D/OpenGL wrapper which stores the WinRT-specific things in a pImpl class that is a C++/CX ref class if compiled for WinRT and a plain class if compiled for Win32. The game’s rendering code just sees a Rasterizer & and can be compiled with C++/CX disabled even:

      ///Uses Direct3D 11 to render polygon-based scenes
      class Direct3D11Rasterizer : public Rasterizer {
    
    #if defined(NUCLEX_GRAPHICS_WINRT)
    
        /// Initializes a new Direct3D 11 polygon renderer
        /// Window into which to render
        public: NUCLEX_GRAPHICS_API Direct3D11Rasterizer(Windows::UI::Core::CoreWindow ^window);
    
    #else
    
        /// Initializes a new Direct3D 11 polygon renderer
        /// Handle of the window into which to render
        public: NUCLEX_GRAPHICS_API Direct3D11Rasterizer(HWND windowHandle);
    
    #endif
    
        /// Frees all resources owned by the rasterizer
        public: NUCLEX_GRAPHICS_API virtual ~Direct3D11Rasterizer();
    
        /// ...all methods required by the 'Rasterizer' interface ...
    
    #if defined(NUCLEX_GRAPHICS_WINRT)
        /// Stores privates implementation details
        private: ref struct Impl;
        /// Private implementation details of the rasterizer
        private: Impl ^impl;
    #else
        /// Stores privates implementation details
        private: struct Impl;
        /// Private implementation details of the rasterizer
        private: Impl *impl;
    #endif
    
      };
    

    I did the same for input devices and the file system, so that apart from having to write a new launcher application that handles the platform-specific startup code, I use the exact same code on WinRT, Win32, Linux and Android.

    The biggest change I’m going through actually isn’t WinRT but C++11 — so far events, critical sections and the thread pool were my bread and butter, but now I’m reeducating myself to use futures, promises and, for the first time ever, portable lock-free data structures thanks to std::atomic :)

  3. Ed Ball says:

    Thank you!

  4. Yu Li Shein says:

    I couldn’t run your sample code. Error is shown as follow:
    [error C3984: 'DirectXBase' : a non-value type cannot have any public data members 'm_dwriteFactory' (Source\DirectXBase.cpp)]

    Is it possible to load c,c++ WinRt dll from Metro Style c#. pls reply to me that it is possible or not. If you can give me the whole source code. It will be great thankful!!

  5. Cygon says:

    This is the whole source code. It was written in Visual Studio 11 Beta (that’s why the file you downloaded is called Nuclex.VS11Beta.ConsumeDllWinRT.Demo.7z) and won’t work in Visual Studio 2012 RTM.

    I’ll update this when Window 8 RTM is released. Until then, just follow the instructions above.

    Using a C++ WinRT Component DLL in C# is possible. But because C++ doesn’t compile to MSIL, you will have to provide 3 versions of your app: x86, x64 and ARM – at least if you want it to work on all Windows 8 tablets out there.

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>

Please copy the string ifQ3FN to the field below:

Social Widgets powered by AB-WebLog.com.