不幸的是,来自 0lli.rocks的答案要么已过时,要么不完整。我的同事帮助我完成了这项工作,坦率地说,其中一两个实施细节并不明显。这个答案纠正了差距,应该可以直接复制到 Visual Studio 2017 中供您自己使用。
警告:我无法让它为 C++/WinRT 工作,仅供参考。由于接口不明确导致的各种编译错误IUnknown
。我也遇到了问题,使其仅适用于库实现,而不是在应用程序的主体中使用它。我专门尝试按照 0lli.rocks 的说明进行操作,但始终无法编译。
步骤 01:创建 C# 库
这是我们将用于演示的那个:
using System;
using System.Runtime.InteropServices;
namespace MyCSharpClass
{
[ComVisible(true)] // Don't forget
[ClassInterface(ClassInterfaceType.AutoDual)] // these two lines
[Guid("485B98AF-53D4-4148-B2BD-CC3920BF0ADF")] // or this GUID
public class TheClass
{
public String GetTheThing(String arg) // Make sure this is public
{
return arg + "the thing";
}
}
}
步骤 02 - 为 COM 可见性配置 C# 库
子步骤 A - 注册 COM 互操作性
子步骤 B - 使程序集 COM 可见
.tlb
第 3 步 - 为文件构建库
你可能只想这样做Release
,AnyCPU
除非你真的需要更具体的东西。
第 4 步 - 将.tlb
文件复制到 C++ 项目的源位置
第 5 步 - 将.tlb
文件导入您的 C++ 项目
#include "pch.h"
#include <iostream>
#include <Windows.h>
#import "MyCSharpClass.tlb" raw_interfaces_only
int wmain() {
return 0;
}
第 6 步 - 当 Intellisense 失败时不要惊慌
它仍然会建立。一旦我们将实际类实现到 C++ 项目中,您将看到更多红线代码。
第 7 步 - 构建您的 C++ 项目以生成.tlh
文件
一旦您第一次构建,此文件将进入您的中间对象构建目录
第 8 步 - 评估.tlh
文件以获取实施说明
这是.tlh
在中间对象文件夹中生成的文件。不要编辑它。
// Created by Microsoft (R) C/C++ Compiler Version 14.15.26730.0 (333f2c26).
//
// c:\users\user name\source\repos\consoleapplication6\consoleapplication6\debug\mycsharpclass.tlh
//
// C++ source equivalent of Win32 type library MyCSharpClass.tlb
// compiler-generated file created 10/26/18 at 14:04:14 - DO NOT EDIT!
//
// Cross-referenced type libraries:
//
// #import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb"
//
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace MyCSharpClass {
//
// Forward references and typedefs
//
struct __declspec(uuid("48b51671-5200-4e47-8914-eb1bd0200267"))
/* LIBID */ __MyCSharpClass;
struct /* coclass */ TheClass;
struct __declspec(uuid("1ed1036e-c4ae-31c1-8846-5ac75029cb93"))
/* dual interface */ _TheClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(_TheClass, __uuidof(_TheClass));
//
// Type library items
//
struct __declspec(uuid("485b98af-53d4-4148-b2bd-cc3920bf0adf"))
TheClass;
// [ default ] interface _TheClass
// interface _Object
struct __declspec(uuid("1ed1036e-c4ae-31c1-8846-5ac75029cb93"))
_TheClass : IDispatch
{
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall get_ToString (
/*[out,retval]*/ BSTR * pRetVal ) = 0;
virtual HRESULT __stdcall Equals (
/*[in]*/ VARIANT obj,
/*[out,retval]*/ VARIANT_BOOL * pRetVal ) = 0;
virtual HRESULT __stdcall GetHashCode (
/*[out,retval]*/ long * pRetVal ) = 0;
virtual HRESULT __stdcall GetType (
/*[out,retval]*/ struct _Type * * pRetVal ) = 0;
virtual HRESULT __stdcall GetTheThing (
/*[in]*/ BSTR arg,
/*[out,retval]*/ BSTR * pRetVal ) = 0;
};
} // namespace MyCSharpClass
#pragma pack(pop)
在该文件中,我们看到了我们想要使用的公共方法的这些行:
virtual HRESULT __stdcall GetTheThing (
/*[in]*/ BSTR arg,
/*[out,retval]*/ BSTR * pRetVal ) = 0;
这意味着导入的方法将需要一个类型为 的输入字符串BSTR
,以及一个指向BSTR
输出字符串的指针,导入的方法将在成功时返回该指针。您可以像这样设置它们,例如:
BSTR thing_to_send = ::SysAllocString(L"My thing, or ... ");
BSTR returned_thing;
在我们可以使用导入的方法之前,我们必须构造它。从.tlh
文件中,我们看到以下几行:
namespace MyCSharpClass {
//
// Forward references and typedefs
//
struct __declspec(uuid("48b51671-5200-4e47-8914-eb1bd0200267"))
/* LIBID */ __MyCSharpClass;
struct /* coclass */ TheClass;
struct __declspec(uuid("1ed1036e-c4ae-31c1-8846-5ac75029cb93"))
/* dual interface */ _TheClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(_TheClass, __uuidof(_TheClass));
//
// Type library items
//
struct __declspec(uuid("485b98af-53d4-4148-b2bd-cc3920bf0adf"))
TheClass;
// [ default ] interface _TheClass
// interface _Object
首先,我们需要使用类的命名空间,即MyCSharpClass
接下来,我们需要从命名空间中确定智能指针,即_TheClass
+ Ptr
;这一步并不明显,因为它不在.tlh
文件中。
最后,我们需要为类提供正确的构造参数,即__uuidof(MyCSharpClass::TheClass)
结束,
MyCSharpClass::_TheClassPtr obj(__uuidof(MyCSharpClass::TheClass));
第 9 步 - 初始化 COM 并测试导入的库
您可以使用CoInitialize(0)
特定的 COM 初始化程序或任何您的特定 COM 初始化程序来执行此操作。
#include "pch.h"
#include <iostream>
#include <Windows.h>
#import "MyCSharpClass.tlb" raw_interfaces_only
int wmain() {
CoInitialize(0); // Init COM
BSTR thing_to_send = ::SysAllocString(L"My thing, or ... ");
BSTR returned_thing;
MyCSharpClass::_TheClassPtr obj(__uuidof(MyCSharpClass::TheClass));
HRESULT hResult = obj->GetTheThing(thing_to_send, &returned_thing);
if (hResult == S_OK) {
std::wcout << returned_thing << std::endl;
return 0;
}
return 1;
}
再一次,当 Intellisense 出现故障时不要惊慌。你在黑魔法、巫毒和 Thar Be Dragons 的领域,所以继续前进!