You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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字段时,程序无异常运行。

我推测这是编译器优化导致:结构体大小未超过某个阈值时按值处理,触发异常;超过阈值时按引用处理,因此无异常,但没找到相关官方文档。

现提出两个问题:

  1. 为何结构体含2个uint时抛出异常,含3个时却不抛出?
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 15:00:53