0

我使用 PInvoke 从 c# 代码访问 C++ 库。但我在使用以下功能时遇到问题:

UINT32 myfunc(IN char * var1, IN UINT32 var2, INOUT UINT16 * var3, OUT UINT8 * var4)

文档说:

返回参数为错误码

var1 是一个字符串

var2 是一个整数

var3 是一个指向整数的指针(该值将是 var4 的大小,该值将由函数更改)

var4 是指向将由函数填充的缓冲区的指针

当我使用

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int myfunc(string var1, int var2, out int var3, 
    out byte[] var4);

该函数给出以下异常:

托管调试助手“FatalExecutionEngineError”在“C:\Users...\Documents\Projects...\bin\Debug...vshost.exe”中检测到问题。

附加信息:运行时遇到致命错误。错误地址位于线程 0x2edc 上的 0x74292e44。错误代码为 0xc0000005。此错误可能是 CLR 中的错​​误或用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括 COM 互操作或 PInvoke 的用户封送错误,这可能会损坏堆栈。

如果有这个异常的处理程序,程序可以安全地继续。

我尝试使用参数来查看是否可以得到任何东西,并更改了字符集,如下所示:

[DllImport("mydll.dll, CallingConvention = CallingConvention.Cdecl", CharSet = CharSet.Unicode)]
public static extern int myfunc(string var1, int var2, out int var3, out byte[] var4);

在这种情况下,它不会给出 FatalExecutionEngineError,而是函数返回没有错误。但是返回值表明函数发生了内部错误。(我不知道为什么字符集更改会阻止 FatalExecutionEngineError。)

我不确定我做错了什么,请帮助我。

4

2 回答 2

4

p/invoke 声明应如下所示:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int myfunc(
    string var1, 
    uint var2, 
    ref ushort var3, 
    byte[] var4
);

然后你这样称呼它:

string var1 = "...";
uint var2 = ...;
ushort var3 = 1024;//use whatever length buffer you want to pass
byte[] var4 = new byte[var3];
int retval = myfunc(var1, var2, ref var3, var4);
Array.Resize(ref var4, var3);

本机代码的工作方式是它期望调用代码分配缓冲区,var4. 调用代码说明缓冲区在var3.

然后当本机代码返回时,var3包含缓冲区的实际长度。或者可能是复制的字节数。前者更有用。假设前者那么你也许可以这样写:

string var1 = "...";
uint var2 = ...;
ushort var3 = 0;
int retval = myfunc(var1, var2, ref var3, null);
byte[] var4 = new byte[var3];
int retval = myfunc(var1, var2, ref var3, var4);

您应该检查返回值等。

我不能 100% 确定如何调用该函数,因为您的问题中没有足够的信息。至少 p/invoke 签名现在与您的 C++ 代码匹配。

于 2013-01-16T11:47:54.853 回答
1

这是一个潜在的非常危险的函数,它可以通过第 4 个参数任意将垃圾喷射到内存中。如果它设计得很好(手指交叉),那么它应该允许您首先测量所需的缓冲区大小。任意猜测:

[DllImport("mydll.dll, CallingConvention = CallingConvention.Cdecl", CharSet = CharSet.Ansi)]
public static extern int myfunc(string name, ref int var2, ref int buflen, IntPtr buf);

string name = "foo";
int var2 = 42; //???
int len = 0;
int error = myfunc(name, ref var2, ref len, IntPtr.Zero);
if (error == BUFFER_TOO_SMALL) {
    IntPtr buf = Marshal.AllocCoTaskMem(len);
    try {
       error = myfunc(name, ref var2, ref len, buf);
       if (error == OK) {
           var bytes = new bytes[len];
           Marshal.Copy(buf, bytes, 0, len);
           //...
       }
    }
    finally {
       Marshal.FreeCoTaskMem(buf);
    }
}
if (error != OK) handleError(error);

这是相当沉重的猜测。如果该函数实际上不允许您首先询问所需的缓冲区大小,那么您应该更改 C 代码,使其像这样工作。只有这样才能安全地调用它。

于 2013-01-16T12:07:18.753 回答