2

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

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

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

我不知所措。我发布了另一个关于 C++/WinRT 模板与带有 C++/WinRT NuGet 包的 win32 之间的区别的问题。

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

编辑

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

在此处输入图像描述

按照 Simon 的回复,我能够创建一个可以从 Win32 控制台应用程序调用的 C# WinRT 组件。此 C# WinRT 组件会输出 .dll 和 .winmd 我更接近于Simon 发布的关于使用 C++的文章,并设法让它与基本的 C# 函数一起使用。

4

1 回答 1

1

REGDB_E_CLASSNOTREG表示您要求的类(无论是 COM/WinRT 等)未注册/激活系统(托管在 combase.dll 中)。

问题可能来自您尝试使用免注册 WinRT 组件这一事实。

让我们将此示例作为 C# 组件的开始:演练:创建 C#/WinRT 组件并从 C++/WinRT 使用它。因此,只需创建 C# 组件,不要创建 C++/WinRT 应用程序。(我使用 Visual Studio 2019 和 net5.0-windows10.0.19041.0)。

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

如果不构建 C++/WinRT 应用程序,仍需要构建常规 .h 文件才能使用 C# 组件。C++/WinRT 会为您完成这项工作,但由于我们不使用此工具,因此我们必须自己构建它。为此,我们需要另外两个工具winmdidl.exemidlrt.exe您可以从Visual Studio 的开发人员命令提示符中找到它们。.另 请参阅如何:使用 winmdidl.exe 和 midlrt.exe 从 Windows 元数据创建 .h 文件

因此,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'组件。

所以你必须做的在这里解释:免注册 WinRT 的工作原理

您必须向项目中添加一个带有.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>

assemblyIdentity'sname不是很重要,最重要的是fileand activatableClass's name:它必须与主机 dll 名称(这里它必须WinRT.Host.dll由 C#/WinRT 提供)和您尝试激活的类名相同(对应到RuntimeClass_SampleComponent_Example)。

您还必须将所有需要的 C#/WinRT 文件复制到.exe文件旁边。那将是: SampleComponent.dll, Microsoft.Windows.SDK.NET.dll, WinRT.Host.dll, WinRT.Host.runtimeconfig.json, WinRT.Host.Shim.dll, WinRT.Runtime.dll

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

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

于 2021-09-25T10:01:16.847 回答