WinRT Extensions using RuntimeBroker

Have you ever wanted to do something naughty from your lovely well behaved sandboxed WinRT application?  Do you need to call some Win32 API that you don’t have permission to do from WinRT?

Well WinRT itself does this using a separate service called the RuntimeBroker.  Many WinRT objects use RPC to call into this broker process and do those things that need elevated priviledges.

I built a sample application that shows how to do this.  Essentially it goes something like this:

  1. Create a static C++ library containing a MIDL interface and compile it using /winrt to make it a windows runtime (WinRT) interface. 
  2. This generates RPC proxy stuff needed to marshal the interface across processes.  Compile this into a new C++ DLL called “YourInterface.ProxyStub.dll”.  You will register this dll using regsvr32 and this is needed for RPC to work.  This proxy stub and the implementing broker dll will need to live in a location your WinRT app can access, like c:\windows\system32.
  3. Next create a Universal C++ DLL called “YourInterfaceBroker.dll” – this will contain the implementation of the interface and is designed to run in the RuntimeBroker process.  This DLL will be able to call out to Win32 and do interesting things.
  4. Register the broker using a custom registry script.
  5. Lastly create a C# Blank Universal WinRT application to test out your new interface, referencing the *.winmd that was generated by step 1.  When you instantiate your new runtime class it is loaded into the RuntimeBroker process, and your C# app gets back the client proxy to it so each call to that object is marshalled automatically to the RuntimeBroker. 

 See the sample code: WinRTServiceSample.zip.

MIDL Interface Library

In my sample I started with this static C++ library, I added a MIDL file called Microsoft.Service.Sample.idl, this defines the name of the *.winmd so its namespace has to match the filename.  I then used the MIDL Tab in the C++ properties to turn on “Enable Windows Runtime” /winrt compile option.  The *.winmd file is placed in the project output directory. I built it once and this also generates a dlldata.c and *_i.c and *_p.c files.  I added those files to the project.  To compile them it is easier if you just turn off the C++ option for precompiled headers.  I also added the C++ preprocessor defines: WIN32;_WINDOWS;.  So now you have a static library containing the RPC stubs needed later.  For the purposes of this sample app I defined the following simple WinRT interface and runtime class:

import“inspectable.idl”;
import“Windows.Foundation.idl”;
 
#ifndef NTDDI_WIN8
#define NTDDI_WIN8                          0x06020000
#endif
 
namespace Microsoft
{
    namespace Service
    {
        namespace Sample
        {
            [uuid(C595230E-CB68-49EB-ACD1-70EE35B41D01)]
            [version(NTDDI_WIN8)]
            interface IMyService : IInspectable
            {
                HRESULT DoSomethingFun();
            };
 
            [
                version(NTDDI_WIN8),
                activatable(NTDDI_WIN8),
                marshaling_behavior(agile),
                threading(both),
            ]
            runtimeclass MyService
            {
                [default] interface IMyService;
            }
 
        }
    }
}

 

ProxyStub

I then added a C++ Win32 DLL project called MyService.ProxyStub.  This dll has to include the above files again, but this time with the C++ preprocessor definition “REGISTER_PROXY_DLL”, and you need to generate a new GUID for your runtime class id (different from the one in the MIDL file) in a new ProxyEntry.c file as follows:

// {62C41977-4CCC-4D4E-9C8C-A842DB7182E6}

#definePROXY_CLSID_IS { 0x62c41977, 0x4ccc, 0x4d4e,{ 0x9c, 0x8c, 0xa8, 0x42, 0xdb, 0x71, 0x82, 0xe6 } };

 

#defineUSE_STUBLESS_PROXY

#definePROXY_DELEGATION

 

#include<rpcproxy.h>

#include<ObjIdl.h>

 

#include“dlldata.c”

#include“Microsoft.Service.Sample_i.c”

#include“Microsoft.Service.Sample_p.c”

 

This is also easier to compile if you turn off precompiled headers.  You also need to add a Modile Definition file for the DLL containing:

EXPORTS
    DllGetClassObject           PRIVATE
    DllCanUnloadNow             PRIVATE
    DllRegisterServer           PRIVATE
    DllUnregisterServer         PRIVATE

 

You’ll also need to add a VC++ Include Directory pointing at your interface static lib directory where the dlldata.c and *_i.c and *_p.c files got generated.  You will also need to add the linker input “rpcrt4.lib”.  This generates the proxy stub needed for RPC to work.  This dll will be registered using regsvr32.

Broker

Next I added a Universal C++ DLL project called “MyServiceBroker”.  I added a reference to the static library project, I included #include<ObjIdl.h>in the pch.h file and again added the VC++ include directory where the *_h.h file was generated.  I wanted this DLL to work on the IoT SKU of Windows 10, which is why I used Universal DLL, but I want to run this outside an app container so I removed the following from the *.vcxproj file: <AppContainerApplication>true</AppContainerApplication>.  I also added the following linker instructions to each <Link> section:
    <IgnoreSpecificDefaultLibraries>comsuppwd.lib; comsuppw.lib; kernel32.lib; ole32.lib; user32.lib</IgnoreSpecificDefaultLibraries>
    <
AdditionalDependencies>Wlanapi.lib;mincore.lib;onecoreuap.lib; WindowsApp.lib;</AdditionalDependencies>

Lastly I added the a class that implements my new interface as follows:

#pragmaonce
#include
<windows.h>
#include<wrl.h>
#include<MyInterface_h.h>

namespace Microsoft
{
   
namespace Service
    {
       
namespace Sample
        {
           
classCMyService : public Microsoft::WRL::RuntimeClass<ABI::Microsoft::Service::Sample::IMyService, Microsoft::WRL::FtmBase>
            {
               
InspectableClass(RuntimeClass_Microsoft_Service_Sample_MyService, PartialTrust);

            public:
                CMyService();
                ~CMyService();

                IFACEMETHOD(DoSomethingFun)();

            private:
                CMyService(
constCMyService&);
               
constCMyService& operator = (constCMyService&);
            };

            ActivatableClass(CMyService);
        }
    }
}

Then I added an empty implementation the the .cpp file.  I also replaced the contents of the dllmain.cpp file with the following:

#include“pch.h”
#include<windows.h>
#include<wrl.h>
#include<wrl/module.h>


using namespace Microsoft::WRL;


STDAPI DllGetActivationFactory(_In_HSTRINGactivatableClassId, _COM_Outptr_IActivationFactory **factory)
{
   
return Module<ModuleType::InProcDisableCaching>::GetModule().GetActivationFactory(activatableClassId, factory);
}

_Check_return_
STDAPI
DllGetClassObject(_In_REFCLSIDrclsid, _In_REFIIDriid, _Outptr_LPVOIDFAR* ppv)
{
   
return Module<ModuleType::InProcDisableCaching>::GetModule().GetClassObject(rclsid, riid, ppv);
}

STDAPI DllCanUnloadNow()
{
   
HRESULT hr = S_FALSE;
   
if (Module<ModuleType::InProcDisableCaching>::GetModule().GetObjectCount() == 0)
    {
        hr =
S_OK;
    }
   
return hr;
}

STDAPI_(void) DllAddRef()
{
   
Module<ModuleType::InProcDisableCaching>::GetModule().IncrementObjectCount();
}

STDAPI_(void) DllRelease()
{
   
Module<ModuleType::InProcDisableCaching>::GetModule().DecrementObjectCount();
}

STDAPI_(BOOL) DllMain(_In_opt_HINSTANCEhinst, DWORDreason, _In_opt_void*)
{
   
if (reason == DLL_PROCESS_ATTACH)
    {
        DisableThreadLibraryCalls(
hinst);
    }
   
return TRUE;
}

Setup

First you need to copy your *ProxyStub.dll and your *Broker.dll to a place that the WinRT app can access (like c:\windows\system32) otherwise you will get E_ACCESSDENIED when trying to call object from WinRT.

To register the broker I created a custom registry script containing the following.  Notice the GUID used here matches the one I generated for the ProxyEntry.c file earlier.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsRuntime\ActivatableClassId\Microsoft.Service.Sample.MyService]
“ActivationType”=dword:00000000
“CLSID”=”62C41977-4CCC-4D4E-9C8C-A842DB7182E6”
“Threading”=dword:00000000
“TrustLevel”=dword:00000001
“DllPath”=”c:\\Windows\\System32\\MyServiceBroker.dll”

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsRuntime\CLSID\{62C41977-4CCC-4D4E-9C8C-A842DB7182E6}]
“ActivatableClassId”=”Microsoft.Service.Sample.MyService”

Running this from administrator command prompt makes it possible for WinRT to construct your runtime class.  You may need to take ownership of the WindowsRuntime part of the registry in order to do this.  There is a tool floating around called TakeRegistryAdminOwnership.exe that can help with this.

You may also need to install the VC++ debug C++ runtime bits (vcruntime140d.dll and ucrtbased.dll) if you have not already installed that on your test machine.  Use depends.exe to find out what else the proxy and broker are dependent on if they still refuse to load.

C# App

To test out my new brokered service I created a blank C# windows application and added a reference to the generated Microsoft.Service.Sample.winmd file, then added the following lines:

            var service = new Microsoft.Service.Sample.MyService();
            service.DoSomethingFun();

When you run these lines the MyServiceBroker.dll is loaded into the RuntimeBroker.exe process (you can attach to that process using your VS debugger and watch it happen and you can debug your broker that way also).  You will see RPC stuff on the stack when the DoSomethingFun call comes in.  Don’t worry about terminating the RuntimeBroker.exe process, it will auto-restart.

Callbacks

If you need to have your broker do something async and call back to your C# application later you can easily define a new interface, implement that in C# and pass it to your broker as an argument, then the broker can call that interface.  This RPC interface will “timeout” after a minute, so you may need to repeat that process periodically or do a heartbeat kind of thing to keep it alive.  You could build a WinRT component project that wraps all this and exposes a new “Client” interface that turns all this callback stuff into C++/CX events so that it is easier to consume in your C# applications.  Your “Client” interface could also expose C++/CX properties and events so you have a nice modern interface to your new broker. 

Enjoy!
-Chris 

 

2 thoughts on “WinRT Extensions using RuntimeBroker”

  1. Hello Chris,

    This is an excellent tutorial.

    I managed to get your sample working and I also did my own component based on your sample, which works great in Visual Studio 2015 on my development system. I can debug the software running using two different instances of Visual Studio 2015.

    I'm now running into a problem.
    I created an App package from my working C++/CX, XAML, UWP application and installed it on fresh Windows 10 system without Visual Studio installed. I also followed the same steps to install the broker components by taking ownership of the respective registry keys and registering my broker DLLs. When I try to launch the UWP application on the fresh system it fails. The application starts to come up, but than it fails and closes down.

    Using DebugView.exe, I can see the following errors:
    applicationdata.cpp(197)modernexecserver.dll!00007FFE1782E724: (caller: 00007FFE1781B15A) ReturnHr[PreRelease] (11) tid(11f4) 80070002 The system cannot find the file specified.

    There is another line, which I have abbreviated as follows: usermgrcli.dll …. The parameter is incorrect.

    My application actually starts up, but comes down (fails) the moment I call a method in the broker.
    I'm using DEPENDS.EXE on both of the brokered DLLs and I copied the respective DLLs to respective location on my fresh Windows 10 system. However, my application still fails whenever it tries to call a method in the broker.

    Am I missing additional DLLs and registration files?
    I would love some assistance.

    Thank You,
    Clifton Kerr

  2. The proxy stub and the implementing broker dll will need to live in a location your WinRT app can access, like c:windowssystem32, otherwise you get the symptoms you are describing where it gets an access denied trying to call into the broker.

Leave a Reply

Your email address will not be published. Required fields are marked *