您能否从基本 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.exe
和 midlrt.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>
assemblyIdentity
的name
不是超级重要,超级重要的是file
和activatableClass
的name
:它必须与主机dll名称相同(这里必须是C#/WinRT提供的WinRT.Host.dll
)和你要激活的class名称(对应RuntimeClass_SampleComponent_Example
).
您还必须复制所有需要的 C#/WinRT 文件 mess 除了您的 .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
。
现在,它应该可以工作了。
我有一个现有程序 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.exe
和 midlrt.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>
assemblyIdentity
的name
不是超级重要,超级重要的是file
和activatableClass
的name
:它必须与主机dll名称相同(这里必须是C#/WinRT提供的WinRT.Host.dll
)和你要激活的class名称(对应RuntimeClass_SampleComponent_Example
).
您还必须复制所有需要的 C#/WinRT 文件 mess 除了您的 .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
。
现在,它应该可以工作了。