说,我有一个暴露在DLL上的函数原型:
int CALLBACK worker (char* a_inBuf, int a_InLen,
char** a_pOutBuf, int* a_pOutLen,
char** a_pErrBuf, int* a_pErrLen)
我确信从我的C#代码中调用该DLL函数非常容易,但它不适用于此代码:
[DllImport("mydll.dll")]
public static extern int worker(
[In, MarshalAs(UnmanagedType.LPArray)] byte[] inBuf,
int inputLen,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] outBuf,
out int outputLen,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] errBuf,
out int errorLen);
...
int outputXmlLength = 0;
int errorXmlLength = 0;
byte[] outputXml = null;
byte[] errorXml = null;
worker(input, input.Length, output, out outputLength, error, out errorLength);
当我要在非托管库中获取output
和error
的内存(因此取消引用传递的指针)时,我收到了访问冲突:
*a_ppBuffer = (char*) malloc(size*sizeof(char));
如何在我的C#代码中为此函数编写DLLIMPORT
语句?
如何实际调用该函数,以便a_pOutBuf
和a_pErrBuf
可以从null
内访问而不是worker
(即使用真正的双指针)
答案 0 :(得分:7)
您当前的定义无效。 worker
函数在函数内部分配内存并写入该内存。
P/Invoke layer不支持编组以这种方式分配的C样式数组,因为它无法知道调用返回时数组的大小(不像是{{3} }})。
这也是为什么从API函数返回指向数组的指针通常是个坏主意,而Windows API的编写方式是内存分配由调用者处理。
也就是说,您想要将worker
的P / Invoke声明更改为:
[DllImport("mydll.dll")]
public static extern int worker(
[In, MarshalAs(UnmanagedType.LPArray)] byte[] inBuf,
int inputLen,
ref IntPtr outBuf, ref int outputLen,
ref IntPtr errBuf, ref int errorLen);
执行此操作时,您表示您将手动编组阵列(将为您设置outBuf
和errBuf
参数);你将引用传递给指针(双向间接,即你的char**
)然后必须使用其他指标读取它们以进行边界检查(在这种情况下,outputLen
和{{1参数)。
你会在返回时将指针中的数据编组,如下所示:
errorLen
那就是说,你还有另外一个问题。因为内存是在函数内部分配的,所以你必须释放内存。由于您使用int outputXmlLength = 0;
int errorXmlLength = 0;
IntPtr output = IntPtr.Zero;
IntPtr error = IntPtr.Zero;
worker(input, input.Length, ref output, ref outputLength,
ref error, ref errorLength);
// Get the strings.
string outputString = Marshal.PtrToStringAnsi(output, outputLength);
string errorString = Marshal.PtrToStringAnsi(error, errorLength);
来分配内存,因此需要将两个malloc
实例传递回非托管代码,以便IntPtr
调用它们。
如果您使用SAFEARRAY
或LocalAlloc
在非托管代码中分配内存,那么您可以分别使用CoTaskMemAlloc
上的FreeHGlobal
或FreeCoTaskMem
方法免费管理方面的记忆。