gpt4 book ai didi

c# - pinvoke 在调用 C 代码时给出 AccessViolationException

转载 作者:太空宇宙 更新时间:2023-11-04 05:57:42 24 4
gpt4 key购买 nike

我的一位同事用 C 编写了一个库,我想从 C# 调用它。我认为这几乎是正确的,但是当我调用第二个方法/函数时它得到了一个 AccessViolationException。我尝试了一些不同的东西:

  1. 类而不是结构
  2. 将 [MarshalAs(UnmanagedType.FunctionPtr)]​​ 添加到回调/委托(delegate)(这实际上会在第一次调用时抛出异常,而不是第二次调用)
  3. 为结构中的字符串删除 [MarshalAs(UnmanagedType.LPStr)](这将在第二个方法结束大括号后抛出异常)

下面的代码用于 C# 端和我正在使用的 C header 。

C:

#pragma once
#define MAX_FEATURE_LENGTH 30
#define HELPERDLL_API __declspec(dllimport)

struct HelperAttributes {
void(*SaveCallBack) ();
void(*ExitCallBack) ();
char* feature_name;
int attempt_limit;
int check_interval;
};

extern "C"
{
void HELPERDLL_API DoStartUp();
void HELPERDLL_API ReSpecify();
void HELPERDLL_API Initialise(HelperAttributes* attributes);
}

C#:

namespace test
{
public partial class Form1 : Form
{
public delegate void SaveCallBack();
public delegate void ExitCallBack();

public Form1()
{
InitializeComponent();
}

[StructLayout(LayoutKind.Sequential)]
public struct HelperAttributes
{
public SaveCallBack saveCallBack;
public ExitCallBack exitCallBack;
[MarshalAs(UnmanagedType.LPStr)]
public string feature_name;
public int attempt_limit;
public int check_interval;
};

[DllImport("testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int DoStartUp();
[DllImport("testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ReSpecify();
[DllImport("testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Initialise(
[In, MarshalAs(UnmanagedType.LPStruct)]
HelperAttributes attributes
);

private void button1_Click(object sender, EventArgs e)
{
HelperAttributes attributes = new HelperAttributes();

attributes.saveCallBack = saveCallBackDelegate;
attributes.exitCallBack = exitCallBackDelegate;
attributes.feature_name = "XXX";
attributes.attempt_limit = 10;
attributes.check_interval = 30;

Initialise(attributes);
DoStartUp();
}

public void saveCallBackDelegate()
{
this.textBox1.Text = "save callback made";
}

public void exitCallBackDelegate()
{
this.textBox1.Text = "exit callback made";
}
}
}

最佳答案

    HelperAttributes attributes = new HelperAttributes();

那是非常非常麻烦的。您在此代码中有明显的内存管理问题。您在此处分配的结构的生命周期非常有限。它仅在 Click 事件处理程序方法的持续时间内有效,最多纳秒。这以不止一种方式爆炸:

  • C 代码不能存储传递的指针,它必须复制结构。它可能不会那样做。你现在有一个“悬挂指针”,一个臭名昭著的 C 错误。稍后取消引用指针会产生任意垃圾。

  • 您的代码创建并分配给结构的 saveCallback 和 exitCallback 成员的委托(delegate)对象无法存活。垃圾收集器无法发现 C 代码要求它们保持事件状态。因此,下一次垃圾收集会销毁对象,当 C 代码进行回调时,Big Kaboom。另请注意,如果它们实际采用参数,您必须使用 [UnmanagedFunctionPointer] 声明它们以使它们成为 Cdecl。

解决这些问题的方法不止一种。到目前为止,最简单的方法是将变量移出方法并将其声明为static。这使它在程序的生命周期内有效。垃圾收集器总是可以通过这种方式看到对委托(delegate)对象的引用。但是,您不能绕过修复 C 代码并让它制作副本的需要,此结构不可 blittable,并且 C 代码获取指向 pinvoke 编码器创建的临时副本的指针。一旦 Initialise() 返回,它就会变成垃圾。

关于c# - pinvoke 在调用 C 代码时给出 AccessViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24205025/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com