如何从C#中调用一个具有char []作为OUT参数的非托管函数? [英] How do I call an unmanaged function that has a char[] as OUT parameter from C#?

查看:93
本文介绍了如何从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);

当我要在我的非托管库中获取outputerror的内存时出现访问冲突(因此取消对传递的指针的引用):

*a_ppBuffer = (char*) malloc(size*sizeof(char));

  1. 如何在我的C#代码中为此功能编写DLLIMPORT语句?

  2. 我实际上如何调用该函数,以便可以从worker内部访问a_pOutBufa_pErrBuf而不是访问null(即使用真正的双指针)?

解决方案

您当前的定义将不起作用. worker函数是在函数内部分配内存并写入该内存.

P/调用层不支持封送处理C样式以这种方式分配的数组,因为它无法知道调用返回时数组的大小(与

执行此操作时,表示要手动整理阵列(将为您设置outBuferrBuf参数);您正在将引用传递给指针(双向重定向,即您的char**),然后必须使用其他指示器进行边界检查(在本例中为outputLenerrorLen参数)从指针中读取它们. /p>

您将在返回时将数据从指针中封送,如下所示:

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 ,那么您可以使用 FreeHGlobal FreeCoTaskMem 方法分别在 Marshal上免费托管端的内存.

Say, I've got that prototype of a function that is exposed on a DLL:

int CALLBACK worker (char* a_inBuf, int a_InLen,
                     char** a_pOutBuf, int* a_pOutLen,
                     char** a_pErrBuf, int* a_pErrLen)

I'm sure that it's ridiculously easy to call that DLL function from my C# code but it doesn't work with this code:

[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);

I get an access violation when I'm going to fetch the memory for output and error inside my unmanaged library (and therefore de-reference the passed pointer):

*a_ppBuffer = (char*) malloc(size*sizeof(char));

  1. How do I write the DLLIMPORT statement in my C# code for this function?

  2. How do I actually call the function so that a_pOutBuf and a_pErrBuf are accessible and not null from within worker (i.e. use a real double pointer)?

解决方案

Your current definition will not work. The worker function is allocating memory inside of the function and writing to that memory.

The P/Invoke layer does not support marshaling C-style arrays that are allocated in this way, as it has no way of knowing just how large the array will be when the call returns (unlike say, a SAFEARRAY).

That's also why returning pointers to arrays from API functions is generally a bad idea, and the Windows API is written in such a way that the memory allocation is handled by the caller.

That said, you want to change the P/Invoke declaration of worker to this:

[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);

In doing this, you're indicating that you're going to marshal the arrays manually (the outBuf and errBuf parameters are going to be set for you); you're passing the reference to the pointer (double-indirection, that's your char**) and then have to read from them using other indicators for bounds checking (in this case, the outputLen and errorLen parameters).

You would marshal the data out of the pointers upon return like so:

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);

That said, you have another problem. Because the memory was allocated inside the function, you have to free the memory. Since you're using malloc to allocate the memory, you need to pass the two IntPtr instances back to unmanaged code in order to have free called on them.

If you were allocating memory in unmanaged code using LocalAlloc or CoTaskMemAlloc then you could use the FreeHGlobal or FreeCoTaskMem methods respectively on the Marshal class to free the memory on the managed side.

这篇关于如何从C#中调用一个具有char []作为OUT参数的非托管函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆