问题简介:
我必须通过提供DLL文件,LIB文件和c函数头文件的API来控制某个设备,其函数被声明为 dllimport 。
当我在C ++项目中使用API时,一切正常 - 我包含了头文件,lib,dll,并调用了头文件中声明的函数。
当尝试使用[DllImport]属性从C#.NET项目调用这些函数时问题开始:使用确切的名称和参数声明函数,并且运行代码不会抛出任何异常。然而,设备根本没有响应,就像从未实际调用过函数一样。
如何在C标头中声明:
int __declspec(dllimport) Init_Card_RTx(unsigned short device_num, unsigned short enabled_channels, unsigned short xmt_channels);
如何在C#中声明:
[DllImport(Path, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Init_Card_RTx")]
public static extern int Init_Card_RTx(UInt16 device_num, UInt16 enabled_channels, UInt16 xmt_channels);
问题:
答案 0 :(得分:4)
是否因为标题中的函数被声明为dllimport?
可能。需要导出这些功能。它们是否被导出取决于它们是如何由任何人提供给你的DLL编译的。我猜它们应该是(或者C ++代码会尝试导入它们并失败) 在对此类问题进行故障排除时,我要做的第一件事就是在Dependency Walker中加载DLL,它将显示所有导出的函数。如果它们没有显示,则它们不会被导出,而C#也无法调用它们。如果它们出现了,那么你没问题,你不需要更改任何C代码或创建任何包装函数。
在这种情况下,我是否必须使用声明为dllexport的C ++函数包装DLL?
没有。 C#只能使用DllImport调用C函数。导出函数时,C ++会name mangling,这会使事情变得一团糟,而且通常不可行。
您需要做的是以某种方式导出您的功能。我的猜测是它们已经被导出了,但如果没有,你可以制作一些像这样的包装函数。
int __declspec(dllexport) Init_Card_RTx(unsigned short device_num, unsigned short enabled_channels, unsigned short xmt_channels) {
// just call the other function here
}
这些应该导出函数,你应该看到它出现在依赖walker中。
可以从C#访问C DLL所需的步骤是什么?
CLR必须能够找到dll(通常只是将它放在与C#exe相同的目录中),并且必须将这些函数导出为C函数。这就是它。
在C#项目中,我是否还必须包含LIB文件?不只是DLL文件本身?
您根本不需要在C#项目中包含任何内容,只需要public static extern
包装函数及其DllImport
属性。只要CLR可以在运行时找到dll,这就是你所需要的。如果在运行时找不到它,则在调用第一个导入的方法时会出现异常。
PS:获得依赖步行者。我不能为这种事情推荐它更多
答案 1 :(得分:0)
这种事总是痛苦的。您必须确保传递的数据的字节结构与您期望的DLL完全匹配。 C#中的本机结构不知道它们是从外部传递的,因此您必须更严格地构造代码。
有关详细信息,请参阅此处:
http://msdn.microsoft.com/en-us/library/awbckfbz(v=vs.110).aspx
答案 2 :(得分:0)
函数声明为dllimport
的原因是(如果我错了,请纠正我)你的卡供应商提供的 ,这没关系,因为 {{只有在构建DLL时才需要1}}。
dllexport
第一个参数[DllImport(Path, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Init_Card_RTx")]
仅用于ilustration目的,我是不是很好?
您可以使用depends.exe或PE Explorer(http://www.heaventools.com/download-pe-explorer.htm)打开DLL并查看DLL的导出表吗?
我问这个是因为函数名可能会被修饰,如果DLL被编译为C ++而不是普通的C,就会发生这种情况。
答案 3 :(得分:0)
尝试像这样声明它(你只在CPP文件中执行一次,而不是标题)
extern "C" __declspec(dllexport) int __stdcall Init_Card_RTx(unsigned short device_num, unsigned short enabled_channels, unsigned short xmt_channels)
{
//
}
并在C#中:
[DllImport("MyDll.dll")]
int Init_Card_RTx(ushort device_num, ushort enabled_channels, ushort xmt_channels);
你需要导出它并用extern“C”声明。 __stdcall很方便,因为它是.net端的默认值。我的C ++项目中有一个宏,以确保每次都输入相同的宏。
是的,得到依赖行者......