Site icon 지락문화예술공작단

Calling WinRT Components from a Win32 process via the Desktop Bridge

Calling WinRT Components from a Win32 process via the Desktop Bridge

In today’s post, we are covering another step you can take on your journey across the Desktop Bridge: specifically migrating business logic to Windows Runtime Components, aka WinRT Components. Previously, Windows only supported calling OS provided WinRT components from Win32 applications. Any attempt to call user-defined (aka 3rd party) WinRT components would fail because the Win32 application did not have package identity, and thus there was no way to register the component with the system at installation time, nor any way for the system to find the component at runtime.

This limitation is solved because Win32 applications on the Desktop Bridge now have identity and are registered with the OS, including any Windows Runtime Components that are part of the package. In the Windows 10 Fall Creators Update, the Desktop Bridge supports this functionality, including support for both In-Process Servers and Out-Of-Process Servers.

Code sharing – Why WinRT Components vs other options

There are many different ways to share code in your application, so what you choose depends upon your scenarios. At a high level, here are a few ways as they relate to UWP and the Desktop Bridge:

Authoring new code or moving code into a Windows Runtime Component allows code reuse between the AppContainer and Win32 processes in the same package. While you can reuse existing DLLs in your AppContainer process by calling LoadPackageLibrary by moving to a Windows Runtime Component, you gain greater reusability because of better language interoperability (Native C/C++, managed code with C# & VB and Javascript) and Visual Studio integration across all your projects. Additionally, WinRT components support an out-of-process activation model that enables greater robustness for your app.

How does it work?

Because applications on the Desktop Bridge have a manifest, the registration entries for the WinRT Component are the same as you would use for a UWP application – by using the InProcessServer and OutOfProcessServer extensions. These extensions register the ActivatableClassId and its implementation binary with your package, so when your application attempts to activate the class, the system can find it.

In-Process Servers

This feature now allows developers to easily share code between Win32 apps and UWP apps running in AppContainer that can be loaded In-Proc. The component is built the same, e.g. Create a new WinRT Component project in VS, and the registration in the manifest is exactly the same as for UWP in-process servers. Because there is no manifest schema change required, developers can use existing toolsets in VS2015 or VS2017 to build In-Proc servers, but these solutions can only deploy to a machine running the latest flights of the Fall Creators Update.

Below is an example of an in-proc registration for a C++ WinRT Component, where CPPSimpleMathWinRT.dll is a native implementation of the SimpleMath class.


    <Extension Category="windows.activatableClass.inProcessServer">
      <InProcessServer>
        <Path>CPPSimpleMathWinRT.dll</Path>
        <ActivatableClass ActivatableClassId="SimpleMathWinRT.SimpleMath" ThreadingModel="both" />
      </InProcessServer>
    </Extension>

Below, you’ll see a simple Winforms Calculator sample that leverages a C++ WinRT Component for its math engine.

And this is what it looks like at runtime:

Sample with a C++/CX WinRT Component: https://github.com/Microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/WinFormsWinRTComponent

Sample with a C# WinRT Component: https://github.com/Microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/WinformsManagedWinRTComponent

Out-Of-Process servers

The registration of an OOP server for an application using the Desktop Bridge extensions is very familiar to developers who have registered servers before in UWP. However, there are details to clarify and limitations to be aware of. While OOP servers allow you to share code between your Win32 and AppContainer processes, there are limitations on sharing data between clients — that is reflected in the instancing model of the server. It all depends on your application’s needs as to which instancing model you should leverage.

The instancing behavior of a server is determined by the claims on the process token, specifically whether or not a call to NTCompareToken() for the calling process and a running instance of the server returns true. If they match, then existing instance of the server is used. If they are different, then a new instance of the server is started.

One of the key claims is the app identity. Apps in UWP are defined in the manifest and in most UWP applications submitted to the Store, there is only one App. But on the Desktop Bridge you can have more than one. Another key claim is the trust level of the calling process. On the Desktop Bridge, the package itself is declared with the runFullTrust capability, <rescap:Capability Name=”runFullTrust” />, which allows one or apps to be declared with the FullTrust entrypoint, EntryPoint=”Windows.FullTrustApplication”. Apps using the FullTrust entrypoint can call any API they want. Usually this is your main Win32/.Net executable. I’ll refer to these applications as FullTrust apps.

If you do not have this entrypoint, then the application is running in a lower trust level, called Base Trust, and has additional restrictions in a sandboxed environment called AppContainer, which is typical for an app when you create a UWP app in Visual Studio. These different trust levels result in different claims on the process tokens, and the result is a different instance of the server.  This model is called ActivateAsActivator, or AAA. An example of this registration is provided below, and you’ll note that it is the exactly same as what you’d provide for a UWP application; there is nothing new here for using this instancing model to access the server from your Win32 code:


    &amp;lt;Extension Category=&amp;quot;windows.activatableClass.outOfProcessServer&amp;quot;&amp;gt;
      &amp;lt;OutOfProcessServer ServerName=&amp;quot;Microsoft.SDKSamples.Kitchen.OvenServer&amp;quot; &amp;gt;
        &amp;lt;Path&amp;gt;Microsoft.SDKSamples.Kitchen.exe&amp;lt;/Path&amp;gt;
        &amp;lt;Instancing&amp;gt;singleInstance&amp;lt;/Instancing&amp;gt;
        &amp;lt;ActivatableClass ActivatableClassId=&amp;quot;Microsoft.SDKSamples.Kitchen.Oven&amp;quot; /&amp;gt;
      &amp;lt;/OutOfProcessServer&amp;gt;
    &amp;lt;/Extension&amp;gt;

While the ActivateAsActivator model allows you to share code, creating a separate instance of the server per client can be heavy weight. To mitigate this, UWP introduced a concept called ActivateAsPackage (AAP), which provides a single instancing behavior for servers in the package. This is reflected in the new attribute IdentityType=”activateAsPackage” on the <OutOfProcessServer> element.

There is a limitation in the AAP model however, as you must specify which trust boundary you want the server to run in. The server must be registered for use by the AppContainer processes, or for use by the FullTrust processes. If you want to use the server in both the FullTrust and AppContainer processes, you’ll need to build and register two servers with separate server names and class names, as those names need to be unique per package. To register the server for use by your FullTrust process, a new attribute RunFullTrust=”true” has been added. If you want the server to be used by your AppContainer processes, leave the attribute out.

Both new attributes are under the xmlns:uap5=”http://schemas.microsoft.com/appx/manifest/uap/windows10/5” namespace. An example registration is provided below showing both Win32 and UWP server registrations:

AAP Registration of server for use by Win32, aka FullTrust, processes:


    &amp;lt;Extension Category=&amp;quot;windows.activatableClass.outOfProcessServer&amp;quot;&amp;gt;
      &amp;lt;OutOfProcessServer ServerName=&amp;quot;Microsoft.SDKSamples.Kitchen.OvenServer&amp;quot; uap5:IdentityType=&amp;quot;activateAsPackage&amp;quot; uap5:RunFullTrust=&amp;quot;true&amp;quot;&amp;gt;
        &amp;lt;Path&amp;gt;Microsoft.SDKSamples.Kitchen.exe&amp;lt;/Path&amp;gt;
        &amp;lt;Instancing&amp;gt;singleInstance&amp;lt;/Instancing&amp;gt;
        &amp;lt;ActivatableClass ActivatableClassId=&amp;quot;Microsoft.SDKSamples.Kitchen.Oven&amp;quot; /&amp;gt;
      &amp;lt;/OutOfProcessServer&amp;gt;
    &amp;lt;/Extension&amp;gt;

AAP registration of server for use by UWP processes:


    &amp;lt;Extension Category=&amp;quot;windows.activatableClass.outOfProcessServer&amp;quot;&amp;gt;
      &amp;lt;OutOfProcessServer ServerName=&amp;quot;Microsoft.SDKSamples.Kitchen.OvenServerUWP&amp;quot; uap5:IdentityType=&amp;quot;activateAsPackage&amp;quot;&amp;gt;
        &amp;lt;Path&amp;gt;Microsoft.SDKSamples.KitchenUWP.exe&amp;lt;/Path&amp;gt;
        &amp;lt;Instancing&amp;gt;singleInstance&amp;lt;/Instancing&amp;gt;
        &amp;lt;ActivatableClass ActivatableClassId=&amp;quot;Microsoft.SDKSamples.Kitchen.OvenUWP&amp;quot; /&amp;gt;
      &amp;lt;/OutOfProcessServer&amp;gt;
    &amp;lt;/Extension&amp;gt;

The sample uses the AAP scenario and shows two C# Winforms apps using a OOP WinRT Component, resulting in only one instance of the server executable. The WinRT Component is a modified version of the WRLOutOfProcessWinRTComponent sample from the Universal Windows Samples on github. In this example, both client call the server and call the BakeBread() method. You’ll see from the TaskManager that there is only one instance of the Server.

GitHub link: https://github.com/Microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/WinformsOutOfProcessWinRTComponent

Visual Studio Support

It’s worth calling out a couple details and workarounds in projects created for this solution. First of all, Visual Studio currently does not allow you to add project references from a WinRT Component project to a Win32/.Net project.  You can work around this by unloading the Win32/.Net project and adding the project reference to the project file directly, e.g.:


  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;ProjectReference Include=&amp;quot;..ServerWRLOutOfProcessWinRTComponent_server.vcxproj&amp;quot; /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;

While this adds the reference, you will see a warning in Visual Studio, as this was not previously supported by the OS. We are working with Visual Studio to improve this in a future release, but for now you can ignore the warning.

Second, the samples are using a UWP JavaScript project to handle the app packaging. This technique is noted in the Desktop Bridge Packaging with Visual Studio documentation, and works as a reasonable solution until Visual Studio adds future support. The benefit of this approach is that you can add a reference from your WinRT component to the JavaScript project, and then the Visual Studio build system adds appropriate registrations for package dependencies, including VCLibs and .NetNative, as well as the <InProcessServer> extensions. Visual Studio does not support adding registrations for <OutOfProcessServer> registrations, so you’ll need to add those manually to the manifest

Metadata Based Marshaling – No more Proxy/Stub DLLs!

Finally, in the <OutOfProcessServer> example, we take advantage of the Metadata Based Marshalling feature (MBM) that was introduced in the Windows 10 Anniversary Update (Windows 10 version 1607). This feature has not gotten much attention, but it means that WinRT Component developers do not need to author a proxy/stub class, saving them time and tedious work. This is possible because the WinMD is deployed with the application, and thus the system can identify and marshal the types cross-process for the developer. You will notice that the server code in this example does not include the proxy project nor binaries.

Conclusion

With Windows Runtime Components and the Desktop Bridge, developers can take another step on their journey to migrate business logic to UWP. Windows Runtime Components provide code re-use that can work with either FullTrust processes or UWP processes, and they allow greater interop cross language.

For more information on the Desktop Bridge, please visit the Windows Dev Center.

Ready to submit your app to the Windows Store? Let us know!

Source: Calling WinRT Components from a Win32 process via the Desktop Bridge

Exit mobile version