microsoft / CsWinRT

C# language projection for the Windows Runtime
MIT License
554 stars 107 forks source link

Getting `REGDB_E_CLASSNOTREG` exception in all platforms except x86 #1522

Open OmarBazaraa opened 8 months ago

OmarBazaraa commented 8 months ago

Description I'm trying the "hello world" scenario of consuming a C++/WinRT Runtime Component using a C# console application. The compilation succeeds and the projection types get generated with no problems, but whenever I run the application, I get the following runtime exception: System.Runtime.InteropServices.COMException: 'Class not registered (0x80040154 (REGDB_E_CLASSNOTREG))'

I searched online and some references were saying it might be something wrong/incompatible with platforms. That is why I tried a whole bunch of different project configurations, and the only change that worked for me is when I added <PlatformTarget>x86</PlatformTarget> to my C# project. Changing to <PlatformTarget>x64</PlatformTarget> and running the program as x64 didn't work; only x86 works.

I'm new to the whole WinRT and COM world so I have no clue what is going wrong, and I need to be able to run the application on different platforms. Help is really much appreciated!

Version Info Visual Studio: 17.9.1 Windows SDK: 10.0.22621.0 CppWinRT NuGet: 2.0.220531.1 CsWinRT NuGet: 2.0.7 .NET: 6.0, 7.0, and 8.0 (i.e., I tried on all versions)

Context Here is how I created the projects...

  1. Created a project using "Windows Runtime Component (C++/WinRT)" template
  2. Implemented the runtimeclass as follows:

    // Class.idl
    namespace RuntimeComponent1
    {
    [default_interface]
    runtimeclass Class
    {
        Class();
    
        Int32 MyProperty;
    }
    }
    
    // Class.h
    #pragma once

include "Class.g.h"

namespace winrt::RuntimeComponent1::implementation { struct Class : ClassT { Class() = default;

    int32_t MyProperty();
    void MyProperty(int32_t value);

private:
    int32_t m_data = 0;
};

}

namespace winrt::RuntimeComponent1::factory_implementation { struct Class : ClassT<Class, implementation::Class> { }; }

```cpp
// Class.cpp
#include "pch.h"
#include "Class.h"
#include "Class.g.cpp"

namespace winrt::RuntimeComponent1::implementation
{
    int32_t Class::MyProperty()
    {
        return m_data;
    }

    void Class::MyProperty(int32_t data)
    {
        m_data = data;
    }
}
  1. Created a C# Console Application

  2. Added CsWinRT NuGet

  3. Added reference to the C++/WinRT Windows Runtime Component project

  4. Updated the csproj to add the following property group:

    <PropertyGroup>
    <CsWinRTIncludes>RuntimeComponent1</CsWinRTIncludes>
    <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
    </PropertyGroup>
  5. Updated Program.cs as follows:

    
    using System;

RuntimeComponent1.Class c1 = new ();

c1.MyProperty = 5;

Console.WriteLine(c1.MyProperty);

OmarBazaraa commented 8 months ago

Just tried the C#/WinRT Projection Sample. It works for x64 and gives the same runtime exception when running in x86!

manodasanW commented 8 months ago

For your local repro, can you confirm a couple things: 1) You have the DesktopCompatible property set for both x86 and x64. 2) That the runtime component is being built for x64

With respect to the sample, given building for each architecture requires multiple builds, we only include x64 in the NuGet that is consumed as seen here. That is why you see that x86 errors. Uncommenting out x86 when you do the x86 build should make it pass.

OmarBazaraa commented 8 months ago

@manodasanW, confirming that...

  1. DesktopCompatible property is set for both x86 and x64 platforms.
  2. The runtime component is being built for x64.

But still the same runtime exception. I just want to clarify that if I removed the <PlatformTarget>x86</PlatformTarget> from the C# application, the application crashes at runtime when running in x86. I don't understand why even though the sample didn't include this PlatformTraget property.

Regarding the sample, I did indeed uncomment the x86 part but it was still not working for me until I removed the project from the solution and re-add it again (strange!). 😅

OmarBazaraa commented 8 months ago

For reference, here are the project files...

RuntimeComponent1.vcxproj:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props')" />
  <PropertyGroup Label="Globals">
    <CppWinRTOptimized>true</CppWinRTOptimized>
    <CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
    <CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
    <MinimalCoreWin>true</MinimalCoreWin>
    <ProjectGuid>{6797a314-3617-4d66-bae5-523705c117d0}</ProjectGuid>
    <ProjectName>RuntimeComponent1</ProjectName>
    <RootNamespace>RuntimeComponent1</RootNamespace>
    <DefaultLanguage>en-US</DefaultLanguage>
    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
    <AppContainerApplication>true</AppContainerApplication>
    <ApplicationType>Windows Store</ApplicationType>
    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
    <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.22621.0</WindowsTargetPlatformVersion>
    <WindowsTargetPlatformMinVersion>10.0.22621.0</WindowsTargetPlatformMinVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|ARM">
      <Configuration>Debug</Configuration>
      <Platform>ARM</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|ARM64">
      <Configuration>Debug</Configuration>
      <Platform>ARM64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|ARM">
      <Configuration>Release</Configuration>
      <Platform>ARM</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|ARM64">
      <Configuration>Release</Configuration>
      <Platform>ARM64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <PlatformToolset>v143</PlatformToolset>
    <PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
    <PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
    <PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0'">v140</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
    <GenerateManifest>false</GenerateManifest>
    <DesktopCompatible>true</DesktopCompatible>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
    <UseDebugLibraries>true</UseDebugLibraries>
    <LinkIncremental>true</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
    <UseDebugLibraries>false</UseDebugLibraries>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets">
    <Import Project="PropertySheet.props" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <ItemDefinitionGroup>
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
      <WarningLevel>Level4</WarningLevel>
      <AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
      <!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
      <AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
      <PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateWindowsMetadata>false</GenerateWindowsMetadata>
      <ModuleDefinitionFile>RuntimeComponent1.def</ModuleDefinitionFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
    <ClCompile>
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
    <ClCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
    <Link>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClInclude Include="pch.h" />
    <ClInclude Include="Class.h">
      <DependentUpon>Class.idl</DependentUpon>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="pch.cpp">
      <PrecompiledHeader>Create</PrecompiledHeader>
    </ClCompile>
    <ClCompile Include="Class.cpp">
      <DependentUpon>Class.idl</DependentUpon>
    </ClCompile>
    <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
  </ItemGroup>
  <ItemGroup>
    <Midl Include="Class.idl" />
  </ItemGroup>
  <ItemGroup>
    <None Include="packages.config" />
    <None Include="RuntimeComponent1.def" />
  </ItemGroup>
  <ItemGroup>
    <None Include="PropertySheet.props" />
    <Text Include="readme.txt">
      <DeploymentContent>false</DeploymentContent>
    </Text>
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
    <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
  </ImportGroup>
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
    <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
  </Target>
</Project>

ConsoleApp1.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0-windows10.0.22621.0</TargetFramework>
    <TargetPlatformMinVersion>10.0.22621.0</TargetPlatformMinVersion>
    <!-- Set Platform to AnyCPU to allow consumption of the projection assembly from any architecture. -->
    <Platform>AnyCPU</Platform>
    <!-- Workaround for MSB3271 error on processor architecture mismatch -->
    <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
    <!-- With the following line, the app works only on x86 platform -->
    <!-- Changing this to x64 doesn't make the app work on x64 -->
    <PlatformTarget>x86</PlatformTarget>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\RuntimeComponent1\RuntimeComponent1.vcxproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.7" />
  </ItemGroup>

  <!--CsWinRT properties-->
  <PropertyGroup>
    <CsWinRTIncludes>RuntimeComponent1</CsWinRTIncludes>
    <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
  </PropertyGroup>

</Project>
OmarBazaraa commented 8 months ago

It turned out that the problem is in <Platform>AnyCPU</Platform>. Changing this to <Platforms>x86;x64</Platforms> fixed the problem for me. I'm not sure what is exactly wrong with using AnyCPU, so it will be nice if you can understand more what is the root cause of the issue and try to solve it in future release of CsWinRT.