您能否从基本 Win32 控制台模板应用程序中的 C#/Winrt 组件调用(不是 WinForm/abstractions/wrappers 或使用 C++/Winrt 模板)?

Can you call from a C#/Winrt Component inside a base Win32 console template app (not WinForm/abstractions/wrappers or using the C++/Winrt template)?)

我有一个现有程序 win32 (x86) 控制台应用程序需要调用托管代码(来自 .Net .dll 的 C#)。 .dll 未公开给 COM,但可以从 C#/WinRT 组件调用并由 C++/WinRT 控制台模板应用程序引用,但是 我似乎无法调用即使在安装 C++/WinRT NuGet 包之后,它也可以从 win32 x86 控制台应用程序中获取。我已经构建 运行 this example,但使用的应用程序始终使用 C++/WinRT 模板。当我尝试使用基本 win32 应用程序重现该示例时,出现错误 REGDB_E_CLASSNOTREG Class not registered.

我发现 another example 展示了如何从 win32 应用程序使用 C++/WinRT 组件,无需 注册 类。我以为这就是我的答案。但是,该过程涉及进入应用程序清单并在每次构建 C++/WinRT 组件时通过引用输出的 .dll 文件来指定可激活的 WinRT 类。

问题是:C#/WinRT 组件输出.dll文件,只有.winmd.(请参阅编辑)使用 .winmd 文件,我仍然可以引用 类 并构建我的项目,但我最终遇到相同的 REGDB_E_CLASSNOTREG Class not registered 错误。 我假设 C++/WinRT 和 C#/WinRT 组件都可以编译成某种中间语言(见评论),但为什么 C++/WinRT 输出 .dll.winmd,而 C#/WinRT 仅输出 .winmd 个文件?我尝试使用 WinRT.Runtime.dll 代替输出的 .dll 但这也没有用。

我很茫然。我发布了 another question 关于 C++/WinRT 模板与带有 C++/WinRT NuGet 包的 win32 之间的区别。

主要问题:我能否以某种方式在基本 win32 控制台应用程序中使用 C# .dll(未公开 COM)?

编辑

我意识到我使用的是特定于 UWP 的 C# Windows 运行时组件模板。这可能就是构建时没有输出.dll 的原因。

根据 Simon 的回复,我能够创建一个可以从 Win32 控制台应用程序调用的 C# WinRT 组件。此 C# WinRT 组件 DOES 输出 .dll 和 .winmd。我跟进了 the article Simon 发布的有关使用 C++ 并设法让它与基本 C# 函数一起工作的帖子。

REGDB_E_CLASSNOTREG 表示您要求的 class(无论它是 COM/WinRT 等)不是 registered/known 到激活系统(托管在 combase.dll).

问题可能是由于您正在尝试使用免注册的 WinRT 组件。

让我们以此示例作为 C# 组件的开始:Walkthrough: Create a C#/WinRT component and consume it from C++/WinRT。因此,只需创建 C# 组件,但不要创建 C++/WinRT 应用。 (我用的是Visual Studio2019和net5.0-windows10.0.19041.0)。

注意:C#/WinRT 会生成 .dll(此处 SampleComponent.dll),而不仅仅是元数据。

如果您不构建 C++/WinRT 应用程序,您仍然需要构建常规 .h 文件才能使用 C# 组件。 C++/WinRT 会为你做这件事,但由于我们不使用这个工具,所以我们必须自己构建它。为此,我们需要另外两个工具 winmdidl.exemidlrt.exe,您可以从 Developer Command Prompt for Visual Studio. 中找到它们。另请参阅 How to: Use winmdidl.exe and midlrt.exe to create .h files from windows metadata

因此,如果您按照教程进行操作,那么从 SampleComponent.winmd 中,运行:

winmdidl SampleComponent.winmd

这将创建一个 SampleComponent.idl 文件。现在 运行:

midlrt SampleComponent.idl /metadata_dir "C:\Windows\System32\WinMetadata"

这将创建多个文件(代理、存根等),但我们只需要 SampleComponent.h。现在,像这样创建一个标准的 C++ 控制台应用程序(我不使用 C++/WinRT 我仍然使用 Wrl 来简化我的代码,但这不是强制性的):

#include <windows.h>
#include <stdio.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
#include "path to SampleComponent.h"

#pragma comment(lib, "runtimeobject.lib")

using namespace Microsoft::WRL; // ComPtr
using namespace Microsoft::WRL::Wrappers; // RoInitializeWrapper, HStringReference, HString
using namespace Windows::Foundation; // GetActivationFactory, ActivateInstance

int main()
{
    RoInitializeWrapper init(RO_INIT_MULTITHREADED);
    HRESULT hr = init;

    // all error checks on hr omitted

    ComPtr<SampleComponent::IExampleClass> cls;
    hr = ActivateInstance(HStringReference(RuntimeClass_SampleComponent_Example).Get(), &cls);
    hr = cls->put_SampleProperty(42);

    INT32 i;
    hr = cls->get_SampleProperty(&i);
    wprintf(L"%u\n", i);

    ComPtr<SampleComponent::IExampleStatic> clsStatic;
    hr = GetActivationFactory(HStringReference(RuntimeClass_SampleComponent_Example).Get(), &clsStatic);

    HString str;
    hr = clsStatic->SayHello(str.GetAddressOf());
    wprintf(L"%s\n", str.GetRawBuffer(nullptr));
}

RuntimeClass_SampleComponent_Example 来自 SampleComponent.h 并且应该这样定义:

extern const __declspec(selectany) _Null_terminated_ WCHAR RuntimeClass_SampleComponent_Example[] = L"SampleComponent.Example";

如果你编译它并且 运行,hr 将是 REGDB_E_CLASSNOTREG,因为系统找不到 'SampleComponent.Example' 组件。

所以你必须做的是在这里解释:How Registration-free WinRT Works

您必须向项目添加一个扩展名为 .manifest 的文件(任何名称都应适用于 Visual Studio 的最新版本),例如:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="CppConsoleApp"/>
  <file name="WinRT.Host.dll">
    <activatableClass
        name="SampleComponent.Example"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
  </file>
</assembly>

assemblyIdentityname不是超级重要,超级重要的是fileactivatableClassname:它必须与主机dll名称相同(这里必须是C#/WinRT提供的WinRT.Host.dll)和你要激活的class名称(对应RuntimeClass_SampleComponent_Example).

您还必须复制所有需要的 C#/WinRT 文件 mess 除了您的 .exe 文件。那将是:SampleComponent.dllMicrosoft.Windows.SDK.NET.dllWinRT.Host.dllWinRT.Host.runtimeconfig.jsonWinRT.Host.Shim.dllWinRT.Runtime.dll.

请注意,您可以使用 C++/WinRT 来帮助构建 WinRT.Host.runtimeconfig.json

现在,它应该可以工作了。