如何从C#中调用具有char 作为OUT参数的非托管函数?
说,我有一个暴露在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
可以访问而不是来自worker
内的null
(即使用真正的双指针)?
您当前的定义不起作用。 worker
函数是在函数内部分配内存并写入该内存。
P / Invoke层不支持编组以这种方式分配的C样式数组,因为它无法知道调用返回时数组的大小(不像SAFEARRAY
)。
这也是为什么从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
和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
实例传递回非托管代码,以便可以free
调用它们。
如果使用LocalAlloc
或CoTaskMemAlloc
在非托管代码中分配内存,则可以分别使用Marshal
类上的FreeHGlobal
或FreeCoTaskMem
方法释放托管端的内存。