65.9K
CodeProject 正在变化。 阅读更多。
Home

从 C 编译的 Dll 调用 C# 中的方法

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.84/5 (17投票s)

2004年5月2日

4分钟阅读

viewsIcon

163638

描述了如何通过 C 中编译的 Dll 将 'C' 代码与 C# 进行接口。

网上有一些关于如何从 C# 调用 user.dll 中的 Win32 方法的例子,但我没有找到任何文章明确说明如何先用纯粹的“C”语言创建一个 DLL,然后从 C# 中导入该“C” DLL 中的方法。

 

这自然会引出一个问题:为什么要首先编写一个“C” DLL 呢?

 

嗯,在我的例子中(我会尽量简短),我有一个十五年前的 ISA 卡,它支持八个电子继电器。这些继电器可以通过计算机控制,通过 IO 总线来开关电子设备。这就是乐趣开始的地方,因为你不能像以前在旧版 Windows 中那样直接使用 ANSI “C” 方法 outportb() 将值直接发送到 IO 端口(当时所有进程共享同一个内存堆,这会导致所有那些有趣的访问冲突)。

 

我找到了一个现成的用“C”语言编写的设备驱动程序,它允许进程访问 XP 中的 IO 端口,但为了从 C# 调用 outportb(), 仍然需要我编写一个用“C”语言编写的“适配器” DLL。

 

我知道如何在使用 [DllImport()] 属性导入 user.dll 等中的本机 Win32 方法,但我发现这种技术对我自己用“C”语言编写的 DLL 不起作用。

 

第一个问题是 C# 编译器一直抛出错误,说“无法在 DLL PortIO.dll 中找到名为 OpenIO 的入口点”。仔细查看 OpenIO 中的方法签名后,我找不到任何问题,然后我才想起需要使用 .DEF 文件或之类的东西。我通过 Google 搜索找到了一些零散的信息,但没有解决我问题的文章。

 

总而言之,以下是学到的教训(有些是重温的):

 

-         在使用 .Net 编译器(或者 VC6 编译器)处理扩展名为 *.cpp 的源文件的“C”项目时,你必须通过将公共的(__declspec(dllexport) = 在“C”中是 public 的)方法包含在用 ‘extern “C”’ 定义的块中,或者使用 .DEF 文件。我选择了使用 ‘extern “C”’。名称修饰是 C++ 编译器区分具有相同名称的重载方法的方式。请注意,你不能重载任何包含在 ‘extern “C”’ 中的方法。我相信使用 VC6,可以通过简单地将源文件扩展名更改为 *.c 来关闭名称修饰,但我没有在 VS.Net 中尝试过。

-         你可以通过使用 DllImport 的 EntryPoint 参数来导入名称修饰过的或任何其他公共方法,该参数可以接受一个序号索引或方法名。我在示例代码中同时使用了方法名和序号位置来演示这一点。

-         Dumpbin.exe(随 VS.Net 一起提供)对于排查用“C”语言编写的 DLL 非常有用。如何使用它以及示例输出在源代码的注释中。

-         如果你不在你的“C” DLL 函数定义(头文件 [.h] 中的原型)和声明(*.c 或 *.cpp 文件中的函数体)中包含 __declspec(dllexport) 修饰符,那么该方法将不会被导出为“public”,并且入口点也不会显示在 DLL 的二进制转储中。

 

以下是一个用“C”语言编写的、正确定义的本机 DLL 方法的示例:

 

extern "C"

{

//注意:必须使用 __declspec(dllexport) 来使(导出)方法为 'public'

      __declspec(dllexport) void DoSomethingInC(unsigned short int ExampleParam, unsigned char AnotherExampleParam)

      {

            printf("You called method DoSomethingInC(), You passed in %d and %c\n\r", ExampleParam, AnotherExampleParam);

      }

}//结束 'extern "C"' 以防止名称修饰

 

 以下是 C# 中的导入声明:

[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

public static extern voidDoSomethingInC(ushortExampleParam,charAnotherExampleParam);

以下是 DLL 转储的样子:

文件类型:DLL

段包含 C_DLL_with_Csharp.dll 的以下导出项

00000000 特性

409557E6 时间日期戳 2004 年 5 月 2 日星期日 13:19:50

0.00 版本

1 序号基数

2 函数数量

2 名称数量

序号 提示 RVA 名称

1 0 00011596 ?DoSomethingInMangledC@@YAXPAD@Z

2 1 0001110E DoSomethingInC

摘要

4000 .data

1000 .idata

5000 .rdata

2000 .reloc

1F000 .text

10000 .textbss

© . All rights reserved.