简体   繁体   English

您能否从基本 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)?)

I have an existing program win32 (x86) console app that needs to call managed code (C# from a.Net .dll ).我有一个现有程序 win32 (x86) 控制台应用程序需要调用托管代码(C# 来自 a.Net .dll )。 The .dll is not exposed to COM, but can be called from a C#/WinRT Component and referenced by C++/WinRT Console Template app, BUT I can't seem to call it from a win32 x86 console app even after installing the C++/WinRT NuGet package. I've built and ran this example but the consuming apps are always using the C++/WinRT template. .dll未暴露给 COM,但可以从 C#/WinRT 组件调用并由 C++/WinRT 控制台模板应用程序引用,我似乎无法从 win32 x86 控制台应用程序调用它,即使安装了 C++/ WinRT NuGet package。我构建并运行了这个示例,但使用的应用程序始终使用 C++/WinRT 模板。 When I try to reproduce the example with a base win32 app, I get the error REGDB_E_CLASSNOTREG Class not registered .当我尝试使用基本 win32 应用程序重现示例时,出现错误REGDB_E_CLASSNOTREG Class not registered

I found another example showing how to consume a C++/WinRT component from a win32 app, without registering classes.我找到了另一个示例,展示了如何在注册类的情况下从 win32 应用程序使用 C++/WinRT 组件。 I thought this was my answer.我以为这就是我的答案。 However the process involves going into the application manifest and specify activatable WinRT classes by referencing the outputted .dll file whenever the C++/WinRT component builds.但是,该过程涉及进入应用程序清单并通过在每次构建 C++/WinRT 组件时引用输出的.dll文件来指定可激活的 WinRT 类。

Here's the problem: C#/WinRT components do not output a .dll file, only the .winmd .问题在于: C#/WinRT 组件没有output .dll文件,只有.winmd (see Edit) With the .winmd file, I can still reference the classes and build my project, But I end up with the same REGDB_E_CLASSNOTREG Class not registered error. (请参阅编辑)使用.winmd文件,我仍然可以引用类并构建我的项目,但我最终遇到相同的REGDB_E_CLASSNOTREG Class not registered错误。 I assume both the C++/WinRT and C#/WinRT components would compile into something that is in an Intermediate Language (see comments), but why does C++/WinRT output a .dll and a .winmd , while C#/WinRT only outputs .winmd files?我假设 C++/WinRT 和 C#/WinRT 组件都会编译成某种中间语言的东西(见评论),但为什么 C++/WinRT output a .dll.winmd而 C#/WinRT 只输出.winmd文件? I tried to use WinRT.Runtime.dll in place of the outputted .dll but that didnt work either.我尝试使用WinRT.Runtime.dll代替输出的.dll但这也不起作用。

I'm at a loss.我不知所措。 I posted another question about the difference between the C++/WinRT template vs win32 with C++/WinRT NuGet package.我发布了另一个关于 C++/WinRT 模板与 win32 与 C++/WinRT NuGet package 之间的区别的问题。

Main Problem: Can I use a C# .dll (not COM exposed) in a base win32 console app somehow?主要问题:我能以某种方式在基本 win32 控制台应用程序中使用 C# .dll (不是暴露的 COM)吗?

Edit编辑

I realized that I was using a C# Windows Runtime Component template that was specific to UWP. This might be why there was no outputted.dll when built.我意识到我正在使用特定于 UWP 的 C# Windows 运行时组件模板。这可能是构建时没有输出 .dll 的原因。

在此处输入图像描述

Following Simon's reply, I was able to create a C# WinRT component that can be called from a Win32 console app.根据 Simon 的回复,我能够创建一个 C# WinRT 组件,它可以从 Win32 控制台应用程序调用。 This C# WinRT component DOES output a.dll as well as.winmd.这个 C# WinRT 组件DOES output a.dll 以及 .winmd。 I followed a bit closer to the article Simon posted about consuming with C++ and managed to get it to work with basic C# functions.我仔细阅读了 Simon 发布的关于使用 C++ 的文章,并设法让它与基本的 C# 功能一起使用。

REGDB_E_CLASSNOTREG means the class that you ask for (whatever it is COM/WinRT etc.) is not registered/known to the activation system (hosted in combase.dll). REGDB_E_CLASSNOTREG表示您要求的 class(无论它是 COM/WinRT 等)未注册/已知激活系统(托管在 combase.dll 中)。

The problem probably comes from the fact you're trying to use a registration-free WinRT component.问题可能出在您尝试使用免注册 WinRT 组件这一事实。

Let's take this sample as a start for the C# component: Walkthrough: Create a C#/WinRT component and consume it from C++/WinRT .让我们以此示例作为 C# 组件的开始: 演练:创建 C#/WinRT 组件并从 C++/WinRT 使用它 So, just create the C# component but don't create the C++/WinRT app.因此,只需创建 C# 组件,但不要创建 C++/WinRT 应用。 (I use Visual Studio 2019 and.net5.0-windows10.0.19041.0). (我用的是Visual Studio 2019和.net5.0-windows10.0.19041.0)。

Note: C#/WinRT does produce a.dll (here SampleComponent.dll ), not only metadata.注意:C#/WinRT 确实会生成 a.dll(此处为SampleComponent.dll ),而不仅仅是元数据。

If you don't build the C++/WinRT app, you still need to build a regular.h file to use the C# component.如果您不构建 C++/WinRT 应用程序,您仍然需要构建一个 regular.h 文件以使用 C# 组件。 C++/WinRT does that for you, but since we don't use this tool, we must build it ourselves. C++/WinRT 会为你做这件事,但由于我们不使用这个工具,所以我们必须自己构建它。 For that, we need two other tools winmdidl.exe and midlrt.exe that you'll find from Developer Command Prompt for Visual Studio.为此,我们需要另外两个工具winmdidl.exemidlrt.exe ,您可以从Visual Studio 开发人员命令提示中找到它们。 .See also How to: Use winmdidl.exe and midlrt.exe to create.h files from windows metadata .另请参阅如何:使用 winmdidl.exe 和 midlrt.exe 从 windows 元数据创建 .h 文件

So from the SampleComponent.winmd that you have if you followed the tutorial, run:因此,如果您遵循本教程,则从您拥有的SampleComponent.winmd运行:

winmdidl SampleComponent.winmd

this will create a SampleComponent.idl file.这将创建一个SampleComponent.idl文件。 Now run:现在运行:

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

this will create multiple files (proxy, stub, etc.), but we only need SampleComponent.h .这将创建多个文件(代理、存根等),但我们只需要SampleComponent.h Now, create a standard C++ console app like this (I don't use C++/WinRT I still use Wrl to simplify my code, but this is not mandatory):现在,像这样创建一个标准的 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 is from SampleComponent.h and should be defined like this: RuntimeClass_SampleComponent_Example来自SampleComponent.h ,应该这样定义:

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

If you compile that and run, hr will be REGDB_E_CLASSNOTREG because the system cannot find the 'SampleComponent.Example' component.如果编译并运行,hr 将是REGDB_E_CLASSNOTREG ,因为系统找不到'SampleComponent.Example'组件。

So what you must do is explained here: How Registration-free WinRT Works因此,您必须执行的操作在此处进行了说明: 免注册 WinRT 的工作原理

You must add a file to the project with the .manifest extension (any name should work with recent versions of Visual Studio), for example like this:您必须向项目添加一个扩展名为.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 's name is not super important, what is super important is file and activatableClass 's name : it must be the same as the host dll name (here it must be WinRT.Host.dll which is provided by C#/WinRT) and class name you're trying to activate (corresponding to RuntimeClass_SampleComponent_Example ). assemblyIdentityname不是特别重要,特别重要的是fileactivatableClassname :它必须与主机 dll 名称相同(这里必须是 C#/WinRT 提供的WinRT.Host.dll )和class 您尝试激活的名称(对应于RuntimeClass_SampleComponent_Example )。

You must also copy all the C#/WinRT files mess needed aside your.exe file.您还必须复制 .exe 文件旁边所需的所有 C#/ WinRT文件。 That would be: SampleComponent.dll , Microsoft.Windows.SDK.NET.dll , WinRT.Host.dll , WinRT.Host.runtimeconfig.json , WinRT.Host.Shim.dll , WinRT.Runtime.dll . That would be: SampleComponent.dll , Microsoft.Windows.SDK.NET.dll , WinRT.Host.dll , WinRT.Host.runtimeconfig.json , WinRT.Host.Shim.dll , WinRT.Runtime.dll .

Note you can use C++/WinRT to help building WinRT.Host.runtimeconfig.json .请注意,您可以使用 C++/WinRT 来帮助构建WinRT.Host.runtimeconfig.json

And now, it should work.现在,它应该可以工作了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM