通过引用传递结构导致 AccessViolationException

发布于 2024-08-02 17:36:26 字数 1280 浏览 3 评论 0原文

我的又一个 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;
}

Yet another one of my P/Invoke questions! I have this C function:

int _ei_x_new(ei_x_buff* x);

Essentially, it initializes a new buffer struct. In C#, I have this:

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

ei_x_buff is pretty simple:

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;
}

But when I do this:

ei_x_buff buffer;
Ei.ei_x_new(out buffer);

I get an AccessViolationException:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Do I need to alloc some memory or something? It's such a simple piece of code that I can't see any glaring problems with it.

EDIT: Native code for _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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

昇り龍 2024-08-09 17:36:26

有几件事

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

EDIT

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

Couple of things

  • Use out not ref in the PInvoke signature as you want data marshalled in both directions (into and out of the native function)
  • Can you post the native signature as their could be an issue with the definition. In particular the buff member

EDIT

Just to eliminate one possibility, switch the buff member to be typed IntPtr with no attributes. If that doesn't cause a crash then it's likely a marshalling issue with the string type.

谁对谁错谁最难过 2024-08-09 17:36:26

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

示例:

// 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; 
}

Do yo have mark the buffer as pinned in memory using the fixed statement:

example:

// 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; 
}
oО清风挽发oО 2024-08-09 17:36:26

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

if (x->buff) x->buff[0] = '\0'; 的

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

I think it will work with string too, if you just initialisze the buffer properly on the C side. Keep in mind that the buffer malloc returns may contain garbage, so you should add something like

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

Another thing you may want to double check is that you're using the same calling convention on the native and managed side. The CLR assumes _stdcall unless you specify something else in the DllImport attribute.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文