在C#中调用c dll

时间:2014-01-07 18:57:32

标签: c# c++ c dllimport dllexport

问题简介:

我必须通过提供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);

问题:

  • 那是因为标题中的函数被声明为dllimport吗?
  • 在这种情况下,我是否必须使用声明为dllexport的C ++函数包装DLL?
  • 可以从C#访问C DLL所需的步骤是什么?
  • 在C#项目中,我是否还必须包含LIB文件?不只是DLL文件本身?

4 个答案:

答案 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 ++项目中有一个宏,以确保每次都输入相同的宏。

是的,得到依赖行者......