我正在尝试从非托管c++代码调用。net 4.0 dll。
我遵循了Atul Mani在这篇代码项目文章中的说明。
我构建了。net dll,并遵循了所有的步骤,包括在regasm中注册它。
接下来,我创建了一个非托管的c++项目,并在.cpp文件的开头添加了这一行:#import "D:PathToMyCSharpProjectbinDebugcom.DeviceServices.tlb" rename ("EOF","adoEOF") no_namespace named_guids raw_interfaces_only
当我构建c++项目时,在D:MyCPlusPlusProjectDebug中创建了一个.tlh文件。
接下来,我添加了CodeProject文章中建议的代码,该代码尝试创建一个指向c#库对象的指针。
CoInitialize(NULL); //Initialize all COM Components
// <namespace>::<InterfaceName>
MyCSharpNamespace::IMyCSharpInterfacePtr pMyCSharpInterfacePtr;
"MyCSharpNamespace"是我在c#项目中使用的命名空间。
当我构建c++项目时,我现在得到一个编译错误:错误2:'MyCSharpNamespace':不是一个类或命名空间名称
还有其他错误,因为它不识别IMyCSharpInterfactPtr。
因此,我查看了.tlh文件,其内容如下:
// Created by Microsoft (R) C/C++ Compiler Version 10.00.40219.01 (6478e0c7).
//
// MyCPlusPlusProjectPathdebugcom.deviceservices.tlh
//
// C++ source equivalent of Win32 type library MyCSharpProjectPathbinDebugcom.DeviceServices.tlb
// compiler-generated file created 05/27/14 at 11:52:16 - DO NOT EDIT!
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
//
// Forward references and typedefs
//
struct __declspec(uuid("961b3c24-98f2-400e-8bea-ab357a18d851"))
/* LIBID */ __MyCSharpProject;
//
// Named GUID constants initializations
//
extern "C" const GUID __declspec(selectany) LIBID_MyCSharpProject =
{0x961b3c24,0x98f2,0x400e,{0x8b,0xea,0xab,0x35,0x7a,0x18,0xd8,0x51}};
#pragma pack(pop)
我在网上搜索了一下。tlh文件中应该有什么,在msdn上找到了#import页面。
它说。tlh文件的内容应该包括智能指针声明(即IMyCSharpProjectInterfacePtr)和类型信息声明,这些声明在我的。tlh文件中不存在。
MyCSharpProject声明了一个公共接口,包括一个生成的GUID,并正确构建,我从本文中遵循的所有步骤似乎都成功了。
所以,我的问题是,有人能建议为什么这些定义没有出现在我的。tlh文件,应该在那里?
.tlh文件不可能是错误的,它是从COM服务器的类型库自动生成的。一个明显的缺陷是它相当空,您根本看不到任何声明。
问题是Codeproject.com的文章,在这样的项目中,这是理所当然的,它错过了一个重要的步骤,没有解释什么是真正发生的,经常使用非常糟糕的实践。为了使. net类型能够从COM客户端使用,您必须明确地使其对COM客户端可见。将此属性添加到您想要导出的每个类型:
[ComVisible(true)]
同时应用于接口和类。
像作者建议的那样使用[Guid]属性是非常危险的。在开发库时保留它是可以的,它有助于避免注册表污染,并让您跳过Regasm步骤(并非总是如此),但在发布库之前再次删除它是非常重要的。COM中的一个坚如磐石的规则是,对接口的修改需要一个新的向导,这是一个必要的DLL地狱对策。当你把它留给CLR自动生成时,你会自动得到一个新的。
在开发过程中最好避免在GAC中注册程序集,因为在GAC中留下一个过时的程序集副本会导致太多的事故。在Regasm命令中使用/codebase
选项,这样就不需要这样做了。您可以忽略收到的警告。