WinRT Extensions using RuntimeBroker
Have you ever wanted to do something naughty from your lovely well behaved sand-boxed 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 privileges.
I built a sample application that shows how to do this. Essentially it goes something like this: - Create a static C++ library containing a MIDL interface and compile it using /winrt to make it a windows runtime (WinRT) interface. - 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. - 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. - Register the broker using a custom registry script. - 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:
// {xxxxxxxx-4CCC-4D4E-9C8C-A842DB7182E6}
#define PROXY_CLSID_IS { 0xxxxxxxxx, 0x4ccc, 0x4d4e,{ 0x9c, 0x8c, 0xa8, 0x42, 0xdb, 0x71, 0x82, 0xe6 } };
#define USE_STUBLESS_PROXY
#define PROXY_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 Module 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:
#pragma once
#include <windows.h>
#include <wrl.h>
#include <MyInterface_h.h>
namespace Microsoft
{
namespace Service
{
namespace Sample
{
class CMyService : 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(const CMyService&);
const CMyService& operator = (const CMyService&);
};
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_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory **factory)
{
return Module<ModuleType::InProcDisableCaching>::GetModule().GetActivationFactory(activatableClassId, factory);
}
_Check_return_
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* 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_ HINSTANCE hinst, DWORD reason, _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"="xxxxxxxx-4CCC-4D4E-9C8C-A842DB7182E6"
"Threading"=dword:00000000
"TrustLevel"=dword:00000001
"DllPath"="c:\\Windows\\System32\\MyServiceBroker.dll"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsRuntime\CLSID\{xxxxxxxx-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 called TakeRegistryAdminOwnership.exe
included in the above download that can help with this.
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.
Happy Brokering!