Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a C++WinRT component in C# project error CS0400: The type or namespace name could not be found in the global namespace (are you missing an assembly reference?) #5071

Open
HO-COOH opened this issue Jan 26, 2025 · 15 comments
Labels
area-Projections Request for support of a runtime or language

Comments

@HO-COOH
Copy link

HO-COOH commented Jan 26, 2025

Describe the bug

After 5+ yrs of tooling development, it still does not work out of the box. This is kind of epic. (And UWP project using the same step does work out of the box, without needing to add the nuget)

What I did:

  • Ensure the C# project csproj file has windows sdk version in <TargetFramework>
  • Add a CsWinRT nuget ✅

Steps to reproduce the bug

  1. Create a C++WinRT runtime component project (using the WinUI3 runtime component project template)
  2. Create a C# WinUI3 project, adding a reference to the C++WinRT component project
  3. Add nuget package C# WinRT
  4. Build and see the error.

Expected behavior

No response

Screenshots

No response

NuGet package version

Windows App SDK 1.6.3: 1.6.250108002

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 24H2 (22621, October 2024 Update)

IDE

Visual Studio 2022

Additional context

Repro

@DarranRowe
Copy link

Wouldn't this be a .NET issue?

Older versions of .NET Core and .NET Framework could read the .winmd files directly. However, .NET 5 removed this support.

This is an issue independent of the Windows App SDK because if you start off with a WPF application and then reference a C++ library implemented as a WinRT component created using the Windows Runtime Component (C++/WinRT) template then the exact same thing will happen. The only reason why this isn't a more general problem is because the Windows SDK references are satisfied using the CsWinRT projection provided by the Microsoft.Windows.SDK.NET.Ref NuGet package and the WinUI 3 references are satisfied by the projection provided by the Windows App SDK NuGet package.

Really, the best way for this to be fixed is if the .NET SDK automatically generates a CsWinRT projection if it detects a .winmd reference. The best option is for you to create a CsWinRT projection and then reference that from the .NET application.

@HO-COOH
Copy link
Author

HO-COOH commented Jan 27, 2025

@DarranRowe I am relatively new to C# and honestly don't understand what you are saying. I hope there is a complete documentation about this, that works

@DarranRowe
Copy link

@HO-COOH As a quick and dirty example, I created and uploaded this project.

I kind of used the Generate a C# projection from a C++/WinRT component walkthrough. But I used my own C++/WinRT component. I also completely ignored the create a NuGet package step and directly referenced the projection DLL from the console application.

Another thing to note is that because the application is unpackaged, I used Registration Free WinRT to reference the C++ WinRT component. This is in the app.manifest file.

The only unexpected thing that I needed to do was to also add the CsWinRT in the console application. If I didn't do this then I would get a NETSDK1130 error. The package reference being transitive isn't good enough.

But for full disclosure, I barely use C#, and this was the very first time I have done something like this. I just have a pretty good understanding of how things work on the C++ side and just had a pretty good guess at CsWinRT taking up the same role as CppWinRT.

@HO-COOH
Copy link
Author

HO-COOH commented Jan 27, 2025

@DarranRowe Can you explain how did you make your TestComponentProjection/TestComponentProjection.csproj project ?

@DarranRowe
Copy link

DarranRowe commented Jan 27, 2025

That started as a regular C# Class Library project. It did require manual editing of the project file.

  1. Create a C# .NET Class Library project.
  2. Right click on the project, select Properties and then set Target OS and Target OS version to the required values. Close the Properties tab.
  3. Right click on the project, select Add->Project Reference... and then in the Reference Manager window, select the C++ project. Click OK to accept the change and close the Window.
  4. Right click on the project, select Manage NuGet Packages and install the Microsoft.Windows.CsWinRT.
  5. Delete the .cs file from the project. It is unneeded.
  6. Double click on the project to open the raw project file. Add to the project file:
  <PropertyGroup>
      <!-- Needed to enable CsWinRT in this project.-->
      <CSWinRTIncludes>TestComponent</CSWinRTIncludes>
      <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
  </PropertyGroup>

  <PropertyGroup>
    <!-- Workaround for MSB3271 error on processor architecture mismatch -->
 <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  </PropertyGroup>

Save and then close the tab.

That should be all that is needed.

@RDMacLachlan RDMacLachlan added the area-Projections Request for support of a runtime or language label Jan 27, 2025
@RDMacLachlan
Copy link
Member

@HO-COOH , Was DarrenRowes comment helpful for you?

@HO-COOH
Copy link
Author

HO-COOH commented Jan 31, 2025

@HO-COOH , Was DarrenRowes comment helpful for you?

No, actually. He/She is using Registration Free WinRT, which is not my use case. You should really provide a documenttion, like this one. Also, C++WinRT is already gone from the windows app development >> Platform documentation. What are the reasoning for this?

@DarranRowe
Copy link

DarranRowe commented Jan 31, 2025

The only reason why I am using RegFreeWinRT is because I was showing it off properly in an unpackaged application. I think I already stated that I am still relatively inexperienced in C#, so I was unsure of what was needed and what wasn't. But apparently the RegFreeWinRT registration isn't even needed. I would guess that CsWinRT is bypassing RoActivateInstance, but I couldn't assume that. But in general, the only thing that the registration would affect is how the component is activated. This, in my mind, means that it is outside of the scope of this issue this issue is about getting the application to build.

If the application is packaged then it would be registered in the package manifest normally. I didn't mention this since I thought this was obvious to UWP developers.

Also, the documentation that you state "You should really provide documentation" like also uses RegFreeWinRT. It would also be the CsWinRT team that is responsible for the documentation. Also, as an interesting sidenote, there are ways to sidestep the requirement for RegFreeWinRT here. C++/WinRT looks for components based upon file name and it is possible to rename WinRT.Host.dll. So it is easy enough to not know how the component is registered, and in this case you should have asked if there were other ways of discovering the component without using RegFreeWinRT.

@mominshaikhdevs
Copy link

mominshaikhdevs commented Feb 2, 2025

(Components') Activation is part of COM. And Thus, RegFreeWinRT is a mandatory requirement from COM.

If any of the "Language Projections" are bypassing RegFreeWinRT, then they are not following up COM's requirements in the first place.

@HO-COOH
Copy link
Author

HO-COOH commented Feb 2, 2025

@DarranRowe Your console application crash on exit. And I still couldn't get it working. Would appreciate if you can do the same for a packaged winui3 C# project.

@DarranRowe
Copy link

DarranRowe commented Feb 2, 2025

@HO-COOH

Your console application crash on exit.

2025-02-02.22-45-53.mp4
2025-02-02.22-51-20.mp4

I disagree with this statement. I will not deny that my system is showing a non critical Windows Error Reporting entry in the Event Viewer, but that is why I recorded the desktop, clock included. The timestamp on that report is 22:44, and the clock shows 22:45 and 22:51 when I was running the test application in the various platform/configuration pairs.

I will put together a sample for WinUI 3, but it will take a few days. It is currently late on Sunday, and I am busy during the week. Although for a packaged application, it is registered as a package extension. The example in the package schema reference is what you should be aiming for.

@DarranRowe
Copy link

DarranRowe commented Feb 8, 2025

Well, I will put the project on GitHub later, but as I stated, the only relevant difference was the component had to be registered in the package manifest.

The following is the project's Package.appxmanifest file.

<?xml version="1.0" encoding="utf-8"?>

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap rescap">

  <Identity Name="7528045a-f5c2-46f4-ac6b-5d8eb90f8df8" Publisher="CN=Darran" Version="1.0.0.0" />

  <mp:PhoneIdentity PhoneProductId="7528045a-f5c2-46f4-ac6b-5d8eb90f8df8" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

  <Properties>
    <DisplayName>CsApp1</DisplayName>
    <PublisherDisplayName>Darran</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </Properties>

  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
  </Dependencies>

  <Resources>
    <Resource Language="x-generate"/>
  </Resources>

  <Applications>
    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
      <uap:VisualElements DisplayName="CsApp1" Description="CsApp1" BackgroundColor="transparent" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png">
        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
        <uap:SplashScreen Image="Assets\SplashScreen.png" />
      </uap:VisualElements>
    </Application>
  </Applications>

  <!-- This registers the C++ WinRT component so that it is activatable. -->
  <Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
      <InProcessServer>
        <Path>TestComponent.dll</Path>
        <ActivatableClass ActivatableClassId="TestComponent.Class" ThreadingModel="both" />
      </InProcessServer>
    </Extension>
  </Extensions>

  <Capabilities>
    <rescap:Capability Name="runFullTrust" />
  </Capabilities>
</Package>

The registration in Extensions is what mirrors RegFreeWinRT. Custom XAML controls are just an extension of this, you just have to remember to register all of the runtime classes, including the metadata provider.

If you ever get stuck not knowing what names to register, just remember that the C++ component's .winmd file can be opened with ildasm. This gets installed as part of the Windows SDK. Anyway, if you place the fully qualified name of any activatable or composable runtime classes into the package manifest then it will be properly registered.

Image

It is possible to use the projection, but it contains a lot of extra things which can be confusing.

@DarranRowe
Copy link

DarranRowe commented Feb 8, 2025

The project can be found here.

@HO-COOH
Copy link
Author

HO-COOH commented Feb 9, 2025

@DarranRowe
Just tested. The activation declaration

  <Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
      <InProcessServer>
        <Path>TestComponent.dll</Path>
        <ActivatableClass ActivatableClassId="TestComponent.Class" ThreadingModel="both" />
      </InProcessServer>
    </Extension>
  </Extensions>

is not needed, and the project can still install and run fine.

@DarranRowe
Copy link

DarranRowe commented Feb 9, 2025

It is technically needed for it to be correct.

As I stated previously, it seems like the C# projection is bypassing RoActivateInstance. Since the library the component is in is named after the namespace, the C# projection is able to remove the runtime class name from the end and start looking for libraries named after all or part of the namespace that the runtime class is in. C++/WinRT does this too.

So the way to look at it is if the runtime class' fully qualified name is something like My.Component.Namespace.Class, where Class is the runtime class' name. The projection will first look for My.Component.Namespace.dll, then My.Component.dll, then My.dll and then finally call RoActivateInstance.

The RoActivateInstance call requires the registration, and there are a couple of cases where it can fall back to RoActivateInstance unexpectedly. So even if it isn't needed in these cases, it is always better to have the runtime classes registered in the package manifest.

As an example, I took the sample that I provided but I added a second component to it.

Image

The library has the name AnotherComponent.dll but the root namespace is actually MyComponent.

Image

namespace MyComponent
{
	[default_interface]
	runtimeclass Test
	{
		Test();

		Int32 Add(Int32 left, Int32 right);
	}
}

This means there is no link between the namespace and the library name.

2025-02-09.16-43-35.mp4

This video shows what happens when there is an attempt to activate the component. I step into it using the debugger to make sure that the exception is properly shown in the debugger. This fails with a REGDB_E_CLASSNOTREG error.

2025-02-09.16-44-20.mp4

This video shows what happens when the component is registered in the package manifest file. The component activates normally.

If you wish, I will merge the changes and push them to the repository so you can see it in action yourself.

--EDIT--

As a bit of an addition to this, especially if there was an attempt to replicate this using a C++/WinRT project, the packaging system does something interesting when there is a direct component dependency. The project system automatically makes a reference in the package to the component. In other words, the package extension is automatically filled in based upon the .winmd file. To see the difference, compare the Package.appxmanifest file to the output AppxManifest.xml file. Annoyingly/ironically, the easiest way of showing this in action is with registration free WinRT, since this is easier to set up in an unpackaged setting than using manual packaging.

Anyway, to show this in action, I have the following two video files.

2025-02-11.17-00-14.mp4

This shows what happens when the component is unregistered. I show that the registration is commented out in the manifest file. Since this was based upon the separate packaging project template, I also show that the application is being run directly by showing that the package layout has not been created or deployed. C++/WinRT loads the library successfully, but a direct call to RoActivateInstance fails.

2025-02-11.17-01-45.mp4

This shows the registration in the manifest file being enabled. Again I show that the packaging project is not being used. This time, RoActivateInstance succeeds. In other words, the projections can bypass some of the inbuilt windows functionality. This may make it seem like some registrations are not needed. However these registrations are needed under the right circumstances.

In the end, I only mention this because if things go wrong then you know how to debug things and are able to rectify the issue. If you control things enough that this never affects you then just ignore this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Projections Request for support of a runtime or language
Projects
None yet
Development

No branches or pull requests

4 participants