7

比如说,我有一个暴露在 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. 如何DLLIMPORT在我的 C# 代码中为此函数编写语句?

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

4

1 回答 1

8

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

P/Invoke 层不支持编组以这种方式分配的C 样式数组,因为它无法知道调用返回时数组的大小(与 a 不同SAFEARRAY)。

这也是为什么从 API 函数返回指向数组的指针通常不是一个好主意,并且 Windows API 的编写方式是内存分配由调用者处理。

也就是说,您想将 P/Invoke 声明更改为worker

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

在这样做时,您表明您将手动编组数组(将为您设置outBufanderrBuf参数);您正在传递对指针的引用(双间接,即您的char**),然后必须使用其他指标从它们中读取以进行边界检查(在本例中为outputLenanderrorLen参数)。

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

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调用它们。

如果您在非托管代码中使用LocalAllocor分配内存,CoTaskMemAlloc那么您可以分别在FreeHGlobal上使用or方法来释放托管端的内存。FreeCoTaskMemMarshal

于 2012-11-19T17:33:35.687 回答