使用C#调用C DLL导致“尝试读取或写入受保护的内存”错误

4

我正在尝试在我的程序中使用zabbix_sender函数。这是我的代码:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct zabbix_sender_value_t
{
    [MarshalAs(UnmanagedType.LPStr)] public String host;
    [MarshalAs(UnmanagedType.LPStr)] public String key;
    [MarshalAs(UnmanagedType.LPStr)] public String value;
}

[DllImport("zabbix_sender.dll", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern int zabbix_sender_send_values(
    [param: MarshalAs(UnmanagedType.LPStr)] String address,
    [param: MarshalAs(UnmanagedType.U2)] ushort port, 
    [param: MarshalAs(UnmanagedType.LPStr)] String source, 
    [param: MarshalAs(UnmanagedType.Struct)] zabbix_sender_value_t values,
    [param: MarshalAs(UnmanagedType.U4)] int count,
    [param: MarshalAs(UnmanagedType.LPStr)] String result
);

static void Main()
{
    zabbix_sender_value_t values;
    values.host = "some_hostname";
    values.key = "agent.version";
    values.value = "2.0.0";
    string res = "";
    zabbix_sender_send_values("172.16.1.1", 10051, "127.0.0.1", values, 1, res);
}

这是一个C函数的定义,我想从动态链接库中调用它:

int zabbix_sender_send_values(
    const char *address, 
    unsigned short port,
    const char *source, 
    const zabbix_sender_value_t *values, 
    int count,
    char **result);

typedef struct
{
    char    *host;
    char    *key;
    char    *value;
}
zabbix_sender_value_t;

执行我的C#代码时出现异常:“试图读取或写入受保护的内存。这通常是其他内存已损坏的迹象。” 我尝试过很多次使用Marshalling参数,但没有成功。
这是我第一次使用本地DLL代码来编写C#程序,我真的不知道现在该怎么办。请问有什么帮助,意见或指示可以找到线索吗?
谢谢

最明显的错误是values参数,你必须将其声明为ref,以得到一个zabbix_sender_value_t。更麻烦的是result参数,你肯定不能将它声明为string,因为字符串是不可变的。*char**是有歧义的。它可能是一个字符串数组,最可能的解释是out IntPtr。可以使用Marshal.PtrToStringAnsi()进行转换。但非常重要的是,你不知道如何释放字符串缓冲区,所以每次调用时都可能泄漏内存。这不是一个友好的互操作函数,请使用C++/CLI。 - Hans Passant
4个回答

1
只是一个随意的猜测,你是否在项目属性中启用了“允许不安全代码”,并尝试将调用包装在unsafe {}子句中?

0

该函数接受char**作为结果(最后)参数,而您正在将ByVal字符串作为此参数发送。字符串对象将转换为char*,因此您应将其声明为ref IntPtrout string(建议在此情况下使用)以将字符串的指针发送到函数。
但主要问题是,您正在将ByVal zabbix_sender_value_t作为values参数发送,而该函数接受此结构的数组。如果您只想发送一个实例,则可以将其声明为数组或ref参数。


0
非常感谢您的所有建议。
我尝试了这个。
[DllImport("zabbix_sender.dll", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern int zabbix_sender_send_values(
     [param: MarshalAs(UnmanagedType.LPStr)] String address,
     [param: MarshalAs(UnmanagedType.U2)] ushort port, 
     [param: MarshalAs(UnmanagedType.LPStr)] String source, 
     ref zabbix_sender_value_t values,
     [param: MarshalAs(UnmanagedType.U4)] int count,
     out IntPtr result
);

并且像这样调用函数:

zabbix_sender_send_values("10.0.236.1", 10051, "10.0.236.12", ref values, 1, out res);
string output = Marshal.PtrToStringAnsi(res);
Console.WriteLine(output);

它能工作,但如果我尝试在调试模式下运行,则会抛出PInvokeStackImbalance异常。这不适合在生产中使用,对吗?

谢谢


0

好的,再次感谢您。我已经完成了这段代码,在调试模式下没有任何异常。仅当我尝试在 x86 平台上构建程序时,异常问题才会出现。当我使用 x64 或 AnyCPU(64 位 DLL)时,一切都正常工作。也许是配置错误?

这是我的最终代码:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct zabbix_sender_value_t
{
    [MarshalAs(UnmanagedType.LPStr)] public string host;
    [MarshalAs(UnmanagedType.LPStr)] public string key;
    [MarshalAs(UnmanagedType.LPStr)] public string value;
}

[DllImport("zabbix_sender.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern int zabbix_sender_send_values(
    [param: MarshalAs(UnmanagedType.LPStr)] string address,
    [param: MarshalAs(UnmanagedType.U4)] int port, 
    [param: MarshalAs(UnmanagedType.LPStr)] string source, 
    [param: MarshalAs(UnmanagedType.Struct)] ref zabbix_sender_value_t values,
    [param: MarshalAs(UnmanagedType.U4)] int count,
    out IntPtr result
);

static void Main()
{
    zabbix_sender_value_t values;
    values.host = "some_hostname";
    values.key = "zbx.trap";
    values.value = "99";
    IntPtr res ;
    unsafe {
        try
        {
            zabbix_sender_send_values("10.0.236.1", 10051, "10.0.236.12", ref values, 1, out res);
            string output = Marshal.PtrToStringAnsi(res);
            Console.WriteLine(output);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接