c# - 来自 PInvoke 的 x64 C 函数中的重叠内存地址

标签 c# c pinvoke

我们有一个 PInvoke,如下所示:

[DllImport(DllName, EntryPoint = "ExternalName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ExternalFunction(IntPtr state, IntPtr inputData, int inputIndex, int inputLength, int tailLength);

C 声明如下:

void * __cdecl ExernalName(void* state, void* inputData, int32_t inputIndex, int32_t inputLength, int32_t tailLength);

当以 x86 运行 .NET 应用程序时,它工作正常。但是,当在 x64 计算机上的 AnyCPU 中运行时,函数参数的内存地址将关闭。

出于示例目的,我们假设 state = 1、inputData = 2、inputIndex = 3 和 inputLength = 4。State 和 InputData 都是 void*,因此它们的长度应为 8 个字节,而其余的应为 4 个字节。

如果我在函数中设置断点,则state的值为2。假设state的内存地址确定为0x00000008。

inputData 的内存地址返回为 0x0000000C,inputIndex 为 0x00000010,inputLength 为 0x00000014。

第一个问题,为什么inputData的内存地址在state之后4个字节,而inputIndex在inputData之后4个字节? state 和 inputData 不应该各为 8 个字节吗?

第二个问题,如果我实际检查值应该在的内存地址,它们就在那里,但是 1 在哪里? 1 位于 0x00000000。

这是怎么发生的?我的错误假设是什么?我做错了什么?

最佳答案

这是由 x64 模式下的调用约定引起的。实际上,x64 中没有 cdecl、stdcall、fastcall - 这些指令将被忽略。

在 Windows 中,x64 模式下只有一种调用约定 - Microsoft x64 调用约定。在此约定中,如果每个参数的大小为 1、2、4 或 8 字节,则前四个参数将在寄存器中传递,而不是通过堆栈传递。同时预留32字节用于调试。

这意味着直接从堆栈中获取输入参数的地址并读取其值是没有意义的。

尝试在控制台上打印值,而不是在调试器中观察其值。另请检查可能强制使用 cdecl 而不是默认 x64 调用约定的编译器选项。

关于c# - 来自 PInvoke 的 x64 C 函数中的重叠内存地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41603029/

相关文章:

将简单的 C#define 转换为 Rust 常量

c++ - Clang 运行时检查 : print backtrace

c# - 使用c#删除外部应用程序的标题栏

c# - 按住右键单击与 Manipulation 事件冲突

c# - 是否有可能将对象保存在内存中以获得比 Redis 或 Memcached 更快的缓存存储?

c - 修改c中字符串的值

c# - 将以空字符结尾的字符串列表从外部函数返回到 .NET

c# - 使用 Protocol buffers/Protobuf 的 PInvoke 通用字符串格式

c# - 霍夫变换问题

c# - 如何将 HTML 传递给 Razor 局部 View ?