【问题标题】:Passing struct by reference causing AccessViolationException通过引用传递结构导致 AccessViolationException
【发布时间】:2009-08-25 03:26:06
【问题描述】:

我的另一个 P/Invoke 问题!我有这个 C 函数:

int _ei_x_new(ei_x_buff* x);

本质上,它初始化了一个新的缓冲区结构。在 C# 中,我有这个:

[DllImport(EIDLL, EntryPoint = "_ei_x_new")]
public static extern int ei_x_new(out ei_x_buff x);

ei_x_buff 很简单:

typedef struct ei_x_buff_TAG {
    char* buff;
    int buffsz;
    int index;
} ei_x_buff;

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ei_x_buff {
    [MarshalAsAttribute(UnmanagedType.LPStr)]
    public string buff;
    public int buffsz;
    public int index;
}

但是当我这样做时:

ei_x_buff buffer;
Ei.ei_x_new(out buffer);

我收到 AccessViolationException

试图读取或写入受保护的内存。这通常表明其他内存已损坏。

我需要分配一些内存吗?这是一段如此简单的代码,我看不出有任何明显的问题。

编辑: _ei_x_new 的本机代码:

// In my wrapper library
DLL_EXPORT int _ei_x_new(ei_x_buff* x) {
    return ei_x_new(x);
}

// In external library being wrapped
int ei_x_extra = 100;

int ei_x_new(ei_x_buff* x)
{
    x->buff = malloc(ei_x_extra);
    x->buffsz = ei_x_extra;
    x->index = 0;
    return x->buff != NULL ? 0 : -1;
}

【问题讨论】:

    标签: c# reference struct pinvoke


    【解决方案1】:

    几件事

    • 在 PInvoke 签名中使用 out 而不是 ref,因为您希望在两个方向(进出本机函数)上编组数据
    • 您能否发布本机签名,因为它们可能是定义的问题。特别是buff成员

    编辑

    只是为了消除一种可能性,将 buff 成员切换为不带属性的 IntPtr 类型。如果这不会导致崩溃,那么很可能是字符串类型的编组问题。

    【讨论】:

    • P/Invoke 签名已经用完了。如果您的意思是“ref not out”,它会产生相同的结果。本机签名在问题中给出: int _ei_x_new(ei_x_buff* x);
    • 哦,你是说结构体的原生签名?我已经编辑了我的问题以显示它。
    • 将其更改为 IntPtr 停止了异常。我可以这样保留它,还是我需要使用其他类型?
    • @David,我会走那条路并添加一个包装器属性,该属性在值上调用 Marshal.PtrToStringAnsi。好奇,本机代码中的字符串是如何分配的?我的意思是记忆
    • 我已经用 _ei_x_new 的完整代码更新了这个问题。看起来根本不像是在处理字符串,真的......
    【解决方案2】:

    您是否使用fixed 语句将缓冲区标记为固定在内存中:

    示例:

    // assume class Point { public int x, y; }
    // pt is a managed variable, subject to garbage collection.
    Point pt = new Point();
    // Using fixed allows the address of pt members to be
    // taken, and "pins" pt so it isn't relocated.
    fixed ( int* p = &pt.x )
    {
        *p = 1; 
    }
    

    【讨论】:

    • 在这种情况下应该不相关
    【解决方案3】:

    我认为它也适用于字符串,如果你只是在 C 端正确初始化缓冲区。请记住,缓冲区 malloc 返回可能包含垃圾,因此您应该添加类似

    如果 (x->buff) x->buff[0] = '\0';

    您可能需要仔细检查的另一件事是,您在本机端和托管端使用相同的调用约定。除非您在 DllImport 属性中指定其他内容,否则 CLR 假定为 _stdcall。

    【讨论】:

      猜你喜欢
      • 2013-06-04
      • 2013-05-12
      • 2011-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      • 1970-01-01
      相关资源
      最近更新 更多