Skip to content

Commit

Permalink
Add targets for hosting support (#665)
Browse files Browse the repository at this point in the history
* add target to pack dlls in authored nupkg; target to add as references in consuming app
* update docs to reflect new authoring support
  • Loading branch information
j0shuams committed Jan 15, 2021
1 parent f5a5255 commit 15942b3
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 110 deletions.
168 changes: 72 additions & 96 deletions docs/authoring.md
Expand Up @@ -2,15 +2,17 @@

## Overview
**Authoring Support is still in preview**
C#/WinRT is working to provide support for authoring Windows Runtime components. You can write a library in C#, and use C#/WinRT's source generator to get a winmd that any WinRT compatible language can use. For example, a library written in C# can be used by a C++ program, via C#/WinRT and C++/WinRT, with just a few tweaks to the C++ project.

C#/WinRT is working to provide support for authoring Windows Runtime components. You can write a library in C#, and specify that it is a `CsWinRTComponent` for C#/WinRT to produce a WinMD that any WinRT compatible language can use. For example, a library written in C# can be used by a C++ program, via C#/WinRT and C++/WinRT.


## References
Here are some resources that demonstrate authoring C#/WinRT components and the details discussed in this document.
1. https://github.com/microsoft/CsWinRT/tree/master/src/Tests/AuthoringTest
https://github.com/microsoft/CsWinRT/tree/master/src/Tests/AuthoringConsumptionTest

2. https://github.com/AdamBraden/MyRandom
2. https://github.com/microsoft/CsWinRT/tree/master/src/Tests/AuthoringConsumptionTest

3. https://github.com/AdamBraden/MyRandom


## Authoring the C# Component
Expand All @@ -23,45 +25,81 @@ Accepted `<TargetFramework>` properties |
`net5.0-windows10.0.17763.0` |

The library you are authoring should specify the following properties in its project file:
```
<PropertyGroup>
<!-- update the Windows API version to reflect your TargetFramework -->
<CsWinRTWindowsMetadata>10.0.19041.0</CsWinRTWindowsMetadata>
<CsWinRTComponent>true</CsWinRTComponent>
<CsWinRTEnableLogging>true</CsWinRTEnableLogging>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GeneratedFilesDir Condition="'$(GeneratedFilesDir)'==''">$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'Generated Files'))</GeneratedFilesDir>
</PropertyGroup>
``` csproj
<PropertyGroup>
<!-- update the Windows API version to reflect your TargetFramework -->
<CsWinRTWindowsMetadata>10.0.19041.0</CsWinRTWindowsMetadata>
<CsWinRTComponent>true</CsWinRTComponent>
<CsWinRTEnableLogging>true</CsWinRTEnableLogging>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GeneratedFilesDir Condition="'$(GeneratedFilesDir)'==''">$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'Generated Files)</GeneratedFilesDir>
</PropertyGroup>
```
And don't forget to include a `PackageReference` to `Microsoft.Windows.CsWinRT`!


## Known Authoring Issues
1. There are some programs you could write as/in your component that aren't available in the Windows Runtime.
We are working on implementing diagnostics in our tool that will catch these errors before a winmd is generated for your component.

2. Not all C# types have been mapped to WinRT types, but we are working on completing this coverage.
## Using your authored component
To use the component in a C# app, the authored component just needs to be added as a project/package reference.

3. Composable type support is still in progress
For native (C++) apps, there are DLLs needed to host your authored component. When you use the automatic nuget packaging on-build support (in Visual Studio) to make a nupkg for your runtime component, the DLLs/WinMD are automatically added to your nupkg, before the ```GenerateNuspec``` MSBuild step.

## Using the authored component
You will need to create a targets file for your component, if you are not already, that imports a CsWinRT targets file. The imported targets file configures the native app to use the hosting dlls at runtime.
This means for your component ```MyAuthoredComponent```, you will need a targets file that has an import statment for ```MyAuthoredComponent.CsWinRT.targets```.
The targets file you need **must** be named ```MyAuthoredComponent.targets```, otherwise NuGet will ignore it.

To use the component in a C# app, the authored component just needs to be added as a project/package reference.
For example, the simplest definition of ```MyAuthoredComponent.targets``` would be:
``` targets
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisDirectory)MyAuthoredComponent.CsWinRT.targets" />
</Project>
```

For native (C++) apps, more steps are needed.
The ```MyAuthoredComponent.CsWinRT.targets``` is added to the package by CsWinRT, you'll just need to add your ```MyAuthoredComponent.targets``` file to the package as well.
Do this by adding the following to ``MyAuthoredComponent.csproj```

Modifications needed:
1. Reference to your authored component in the consuming app
2. Add a manifest file and runtimeconfig file [for native consumption only]
3. Use a MSBuild targets for copying necessary DLLs
``` csproj
<ItemGroup>
<_PackageFiles Include="MyAuthoredComponent.targets" PackagePath="build;buildTransitive"/>
</ItemGroup>
```

**If you are going to write your own nuspec, i.e. not rely on automatic packaging** then the CsWinRT target that adds the hosting dlls to your package will not run, and you should make sure your nuspec contains the following ```file``` entries for ```MyAuthoredComponent``` (note: your TargetFramework may vary). If adding a file entry for ```Coords.targets``` does not work, then update the project file per the above example.

### Add the reference to your authored component
**In Visual Studio** Under the Project node, right click on "References", click "Add Reference", then "Browse" and add the `.winmd` file generated in your authored component.
``` nuspec
<files>
<file src="$(TargetDir)MyAuthoredComponent.dll" target="lib\native\MyAuthoredComponent.dll" />
<file src="$(TargetDir)MyAuthoredComponent.winmd" target="winmd\MyAuthoredComponent.winmd" />

<file src="$(TargetDir)Microsoft.Windows.SDK.NET.dll" target="lib\native\Microsoft.Windows.SDK.NET.dll" />

<!-- Note: you must rename the CsWinRt.Authoring.Targets as follows -->
<file src="C:\Path\To\CsWinRT\NugetDir\buildTransitive\Microsoft.Windows.CsWinRT.Authoring.targets"
target="buildTransitive\MyAuthoredComponent.CsWinRT.targets" />

<file src="C:\Path\To\CsWinRT\NugetDir\build\Microsoft.Windows.CsWinRT.Authoring.targets"
target="build\MyAuthoredComponent.CsWinRT.targets" />

<!-- Include the managed DLLs -->
<file src="C:\Path\To\CsWinRT\NugetDir\lib\net5.0\WinRT.Host.Shim.dll"
target="lib\native\WinRT.Host.Shim.dll" />

<file src="C:\Path\To\CsWinRT\NugetDir\lib\net5.0\WinRT.Runtime.dll"
target="lib\native\WinRT.Runtime.dll" />

<!-- Include the native DLLs -->
<file src="C:\Path\To\CsWinRT\NugetDir\runtimes\win-x64\native\WinRT.Host.dll"
target="runtimes\win-x64\native\WinRT.Host.dll" />

<file src="C:\Path\To\CsWinRT\NugetDir\runtimes\win-x86\native\WinRT.Host.dll"
target="runtimes\win-x86\native\WinRT.Host.dll" />
</files>
```

### For native app (C++) consumption
As part of the native (C++) support, the WinRT Hosting dlls (WinRT.Host and WinRT.Host.Shim) need to be in the same folder as the native executable.
For now, users need a special target of their own so MSBuild can place the hosting dlls in the correct place. But soon we will implement this so that a package reference to C#/WinRT in the authored component is all that is needed.
Install your authored component's package -- this will come with a targets file that automatically adds a reference to the component's WinMD and copies the dlls necessary for native support.

You'll need to use [C++/WinRT](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/intro-to-using-cpp-with-winrt) to consume your API. So make sure you have C++/WinRT installed, and have added `#include <winrt/MyAuthoredComponent.h>` to the file `pch.h` of the native app.

You'll need to author some files to assist the hosting process by the native app: `YourNativeApp.exe.manifest` and `WinRT.Host.runtimeconfig.json`.

Expand All @@ -74,81 +112,19 @@ This process adds the nodes `<Manifest Include=... >` and `<None Include=... >`

You should read the [hosting docs](https://github.com/microsoft/CsWinRT/blob/master/docs/hosting.md) as well, for more information on these files.

You'll need to use [C++/WinRT](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/intro-to-using-cpp-with-winrt) to consume the `winmd` too, so make sure you have it installed, and have added `#include <winrt/YourAuthoredLibrary.h>` to the file `pch.h` of the native app.

In summary, here is the fragment of additions made to the native app's project file:
```
``` vcxproj
<ItemGroup>
<!-- targets to copy the needed DLLs -->
<None Include="Directory.Build.targets" />
<!-- the runtimeconfig.json -->
<None Include="WinRT.Host.runtimeconfig.json">
<DeploymentContent>true</DeploymentContent>
</None>
<!-- the winmd -->
<Reference Include="YourAuthoredLibrary">
<HintPath>..\Path\To\Generated\Files\YourAuthoredLibrary.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
</Reference>
<!-- the manifest -->
<Manifest Include="PosnConsole.exe.manifest">
<Manifest Include="YourNativeApp.exe.manifest">
<DeploymentContent>true</DeploymentContent>
</Manifest>
</ItemGroup>
```

### Copying DLLs Target

1. In the project folder for the component you are authoring, you will need to add a `PropertyGroup` so MSBuild places the proper WinRT Dlls in the output directory. This is needed by the targets file we need to add to C++ apps to copy the WinRT Dlls to the native app's output directory.

This is generally done via a `Directory.Build.targets` file like so:
```
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CopyLocalLockFileAssemblies Condition="$(CsWinRTComponent)==true">true</CopyLocalLockFileAssemblies>
</PropertyGroup>
</Project>
```


2. In your C++ app, add a `Directory.Build.targets` file that copies over the necessary DLLs:
```
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PrepareForRunDependsOn>CopyHostAssets;$(PrepareForRunDependsOn)</PrepareForRunDependsOn>
</PropertyGroup>
<PropertyGroup>
<CsWinRTVersion>1.1.0</CsWinRTVersion>
</PropertyGroup>
<Target Name="CopyHostAssets">
<Copy SourceFiles="$(NuGetPackageRoot)microsoft.windows.cswinrt\$(CsWinRTVersion)\native\$(Platform)\WinRT.Host.dll"
DestinationFolder="$(OutDir)"
UseHardlinksIfPossible="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="$(NuGetPackageRoot)microsoft.windows.cswinrt\$(CsWinRTVersion)\lib\net5.0\WinRT.Host.Shim.dll"
DestinationFolder="$(OutDir)"
UseHardlinksIfPossible="false" SkipUnchangedFiles="true" />
<!-- Note: the following three source paths may not need the $(Platform) entry for your app -->
<Copy SourceFiles="..\YourC#Library\bin\$(Platform)\$(Configuration)\net5.0-windows10.0.19041.0\WinRT.Runtime.dll"
DestinationFolder="$(OutDir)"
UseHardlinksIfPossible="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\YourC#Library\bin\$(Platform)\$(Configuration)\net5.0-windows10.0.19041.0\Microsoft.Windows.SDK.NET.dll"
DestinationFolder="$(OutDir)"
UseHardlinksIfPossible="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\YourC#Library\bin\$(Platform)\$(Configuration)\net5.0-windows10.0.19041.0\YourC#Library.dll"
DestinationFolder="$(OutDir)"
UseHardlinksIfPossible="false" SkipUnchangedFiles="true" />
</Target>
</Project>
</ItemGroup>
```

## Known Authoring Issues
You can follow along [here](https://github.com/microsoft/CsWinRT/issues/663) as we develop authoring support.
45 changes: 45 additions & 0 deletions nuget/Microsoft.Windows.CsWinRT.Authoring.targets
@@ -0,0 +1,45 @@
<Project ToolsVersion="15.0" xmln="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<!-- Add the hosting dlls to references so they get binplaced -->
<ResolveReferencesDependsOn>CsWinRTCopyAuthoringDlls;$(ResolveReferencesDependsOn)</ResolveReferencesDependsOn>
<!-- Add authored component's winmd to references before C++/WinRT runs -->
<BuildDependsOn>CsWinRTAddAuthoredWinMDReference;$(BuildDependsOn)</BuildDependsOn>
</PropertyGroup>

<PropertyGroup>
<HostingSupport-NativeDir>$(MSBuildThisFileDirectory)..\lib\native</HostingSupport-NativeDir>
<HostingSupport-MetadataDir>$(MSBuildThisFileDirectory)..\winmd</HostingSupport-MetadataDir>
<HostingSupport-RuntimesDir>$(MSBuildThisFileDirectory)..\runtimes</HostingSupport-RuntimesDir>
<HostingSupport-IsNative Condition="'$(TargetFramework)' == 'native' OR '$(TargetFramework)' == ''">true</HostingSupport-IsNative>
<HostingSupport-IsArmArch Condition="'$(Platform)' == 'arm' OR '$(Platform)' == 'arm64'">true</HostingSupport-IsArmArch>
</PropertyGroup>

<!-- Happens before building, so C++/WinRT can make the corresponding header files -->
<Target Name="CsWinRTAddAuthoredWinMDReference"
Condition="'$(HostingSupport-IsNative)' == 'true'"
Outputs="@(Reference)">
<ItemGroup>
<Reference Include="$(HostingSupport-MetadataDir)\*" />
</ItemGroup>
</Target>

<!-- Happens when resolving references, so the app can be hosted -->
<Target Name="CsWinRTCopyAuthoringDlls"
Condition="'$(HostingSupport-IsNative)' == 'true'"
Outputs="@(ReferenceCopyLocalPaths)">

<ItemGroup Label="Copy Dlls needed for hosting/using authored components">
<ReferenceCopyLocalPaths Include="$(HostingSupport-NativeDir)\*" />
</ItemGroup>

<ItemGroup Label="Copy the WinRT.Host dll, based on architecture">
<ReferenceCopyLocalPaths Include="$(HostingSupport-RuntimesDir)\win-$(Platform)\native\WinRT.Host.dll"
Condition="'$(Platform)' == 'x64' OR '$(Platform)' == 'x86' OR '$(HostingSupport-IsArmArch)' == 'true'" />

<ReferenceCopyLocalPaths Include="$(HostingSupport-RuntimesDir)\win-x86\native\WinRT.Host.dll"
Condition="'$(Platform)' == 'Win32'"/>
</ItemGroup>
</Target>

</Project>
10 changes: 6 additions & 4 deletions nuget/Microsoft.Windows.CsWinRT.nuspec
Expand Up @@ -23,13 +23,15 @@
<file src="Microsoft.Windows.CsWinRT.props" target="build"/>
<file src="Microsoft.Windows.CsWinRT.targets" target="build"/>
<file src="Microsoft.Windows.CsWinRT.Prerelease*.targets" target="build"/>
<file src="Microsoft.Windows.CsWinRT.Authoring.targets" target="build"/>
<file src="Microsoft.Windows.CsWinRT.Authoring.targets" target="buildTransitive"/>
<file src="readme.txt"/>
<file src="$net5_runtime$" target="lib\net5.0\"/>
<file src="$source_generator$" target="analyzers\dotnet\cs\"/>
<file src="$winrt_host_x64$" target ="native\x64"/>
<file src="$winrt_host_x86$" target ="native\x86"/>
<file src="$winrt_host_arm$" target ="native\arm"/>
<file src="$winrt_host_arm64$" target ="native\arm64"/>
<file src="$winrt_host_x64$" target ="runtimes\win-x64\native"/>
<file src="$winrt_host_x86$" target ="runtimes\win-x86\native"/>
<file src="$winrt_host_arm$" target ="runtimes\win-arm\native"/>
<file src="$winrt_host_arm64$" target ="runtimes\win-arm64\native"/>
<file src="$winrt_shim$" target ="lib\net5.0\"/>
</files>
</package>

0 comments on commit 15942b3

Please sign in to comment.