clr - CLR 如何匹配 P/Invoke 期间导出的名称?

标签 clr pinvoke

我从事的项目需要 .Net 与非托管代码的互操作性。尽管我在 C/C++ 方面拥有丰富的经验,但几周前我开始使用 .Net,而且我对 CLR 处理 P/Invoke 的方式感到惊讶。以下是详细信息。我的同事写了这个函数

__declspec(dllexport) int __stdcall ReadIPWSensor(unsigned int deviceClassId, void *buffer) {...}

我必须从 C# 模块调用它。我将函数导入为

[DllImport("ipw", CallingConvention = CallingConvention.StdCall)]
extern static int ReadIPWSensor(uint deviceClassId, IntPtr buffer);

只是为了找出异常(System.EntryPointNotFoundException,无法在 DLL 'ipw' 中找到名为 'ReadIPWSensor' 的入口点)。我使用DependencyWalker工具,发现该函数导出为?ReadIPWSensor@@YGHIPAX@Z(我的同事忘记将其导出到DEF文件中)。只是为了快速测试(非托管 DLL 编译速度非常慢),我将导入定义更改为:

[DllImport("ipw", EntryPoint = "#22", CallingConvention = CallingConvention.StdCall)]
extern static int ReadIPWSensor(uint deviceClassId, IntPtr buffer);

因为序号是 22。使用新的导入定义成功通过了测试。

我的第一个问题是:处理损坏的函数导出时有哪些好的做法?使用导出序数是一个好的做法吗?

就我而言,我可以访问 C++ 源代码和 DEF 文件,因此我添加了导出并将导入定义改回

[DllImport("ipw", CallingConvention = CallingConvention.StdCall)]
extern static int ReadIPWSensor(uint deviceClassId, IntPtr buffer);

我知道我们的项目中已经使用了另一个函数,并且想将我的代码与现有的代码进行比较。该函数定义为

extern "C" __declspec(dllexport) int __stdcall LoadIPWData(void *buffer)

并导入为

[DllImport("ipw", CallingConvention = CallingConvention.StdCall)]
extern static int LoadIPWData(IntPtr buffer);

令我惊讶的是,DependencyWalker 工具显示该函数已导出为 _LoadIPWData@4(我的同事忘记再次将其导出到 DEF 文件中)。但是,使用此函数不会出现 System.EntryPointNotFoundException 错误。显然,CLR 以某种方式设法解析了正确的名称。似乎有某种后备机制可以让 CLR 找到正确的函数。我可以很容易地想象它对参数的大小求和并正在寻找“function_name@the_sum_of_all_parameter_sizes”,尽管它看起来很简单。

我的第二个问题是:CLR 如何在 P/Invoke 期间匹配导出的函数名称?

在这种情况下,我认为 CLR 非常聪明,它实际上隐藏了一个错误 - LoadIPWData 函数应该可以通过其名称从其他非托管模块访问。也许我有点偏执,但我更喜欢知道 CLR 到底是如何工作的。不幸的是,我所有关于该主题的谷歌搜索都没有结果。

最佳答案

pinvoke 编码器内置了一些常见 DLL 导出命名方案的知识。它知道 __cdecl 函数通常有一个前导下划线,并且 32 位模式下的 __stdcall 通常用前导下划线和尾随 @x 修饰,其中 x 是在堆栈上传递的参数的大小(以字节为单位)。它还知道 winapi 函数在导出时带有额外的 A 或 W,这是一种命名方案,用于区分接受字符串的函数,并且该函数同时有 ansi 和 Unicode 版本。相应的[DllImport]属性是CharSet。它只是尝试所有这些,直到找到匹配项。

它对 C++ 编译器名称修饰规则(又名重整)一无所知,因此您必须使用 extern "C" 手动抑制它。

关于clr - CLR 如何匹配 P/Invoke 期间导出的名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10473310/

相关文章:

c# - 如何正确处理 pinvoke/非托管代码

c# - .NET 中 native 互操作性的代码组织

c# - 如何解锁 ConnectNamedPipe 和 ReadFile? [C#]

c# - 检测事件 RDP session

asp.net - SQL - 两个不同长度的字符串之间的相似性

stored-procedures - 将大量 XML 传输到 CLR 存储过程

c++ - 尝试将 C++ 项目转换为与 CLR 兼容的静态库

c# - 不寻常的垃圾收集模式

c# - 一个字符串在 x64 中占用多少字节?

C#/C++ pInvoke 技巧