.NET中结构体大小对自动封送的影响及相关问题
C# P/Invoke结构体传参异常行为的疑问
我正在开发一个封装非托管C++驱动的C# .NET包装器,核心逻辑是创建包含DllImport入口包装方法的C#类。
该非托管驱动的某函数需要结构体指针,我清楚正确传参方式是使用ref(C#结构体是值类型,默认传值时会复制结构体并完成托管到非托管的封送;而ref会传递结构体的托管地址并自动固定内存,确保非托管代码能正确访问)。
但为做实验,我故意不使用ref,直接传递整个结构体实例。本以为非托管驱动会把这个参数当作指针操作,从而引发内存访问异常,但实际结果却因结构体大小不同而不一致:
- 当结构体仅包含2个
uint字段时,程序抛出异常:System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.' - 当结构体包含3个
uint字段时,程序无异常运行。
我推测这是编译器优化导致:结构体大小未超过某个阈值时按值处理,触发异常;超过阈值时按引用处理,因此无异常,但没找到相关官方文档。
现提出两个问题:
- 为何结构体含2个uint时抛出异常,含3个时却不抛出?
- C#不知道非托管函数的真实原型,为何结构体较大时,传递错误参数(比如非托管期望指针却传结构体实例,或期望结构体却传引用)仍能正常运行无异常?
C++ 驱动代码
Datatypes.h
#pragma once #include"basetsd.h" typedef struct _TestStruct { UINT32 x; UINT32 y; UINT32 z; } TestStruct;
main.cpp
#include<iostream> #include"main.h" #include"basetsd.h" #include"Datatypes.h" using namespace std; int foo(TestStruct p, TestStruct *p2) { p2->x = 10; p.x = 11; cout << "p address 0x" << &p << endl; cout << "p2 value 0x" << &(*p2) << endl; cout << "p2 address 0x" << &p2 << endl; while (1); return 0; }
main.h
#pragma once #include"Datatypes.h" #ifdef _EXPORTING #define DECLSPEC __declspec(dllexport) #else #define DECLSPEC __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { // only need to export C interface if // used by C++ source code #endif DECLSPEC int foo(TestStruct p, TestStruct *p2); #ifdef __cplusplus } #endif
C# 包装器代码
wrapper.cs
using System.Runtime.InteropServices; namespace Wrapper { public struct WTestStruct { public uint x; public uint y; public uint z; public uint c; } internal class wrapper { [DllImport("Driver.dll", EntryPoint = "foo", CallingConvention=CallingConvention.Cdecl)] public static extern int Foo(WTestStruct p, ref WTestStruct p2); } }
main.cs
using System; namespace Wrapper { class main { static void Main() { WTestStruct p = new WTestStruct(); p.x = 1; p.y = 2; p.z = 3; p.c = 4; wrapper.Foo(p, ref p); Console.WriteLine("x: {0}", p.x); Console.WriteLine("Executed"); } } }
内容的提问来源于stack exchange,提问作者AntGeorge
相关产品推荐
相关产品推荐

