Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从C#调用以char[]作为OUT参数的非托管函数?_C#_.net_C_Pinvoke_Marshalling - Fatal编程技术网

如何从C#调用以char[]作为OUT参数的非托管函数?

如何从C#调用以char[]作为OUT参数的非托管函数?,c#,.net,c,pinvoke,marshalling,C#,.net,C,Pinvoke,Marshalling,比如说,我得到了一个函数的原型,它在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")]

比如说,我得到了一个函数的原型,它在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);
当我要为非托管库中的
输出
错误
获取内存时,我遇到访问冲突(因此取消引用传递的指针):

  • 如何在C#代码中为该函数编写
    DLLIMPORT
    语句

  • 我如何实际调用该函数,使
    a_pOutBuf
    a_pErrBuf
    可以从
    worker
    内部访问,而不是
    null
    (即使用真正的双指针)


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

    不支持封送以这种方式分配的C样式数组,因为它无法知道调用返回时数组将有多大(不像a)

    这也是为什么从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);
    
    执行此操作时,您表示将手动封送数组(将为您设置
    extuf
    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


    如果您使用非托管代码分配内存,则可以使用OR方法来释放托管端的内存。

    您可以更改WorksActovior签名吗?@ SimMuniely:不,它是C++程序(已建立API)中使用的DLL。现在也应该从新开发的.net应用程序调用。因此API是固定的。我已经有了一个
    cleanup(void)
    方法,用于清理分配的内存。假设(在非托管
    C++
    下工作)我的DLL的状态在调用
    worker
    cleanup
    之间不会改变。国家是内部控制的。该方法完全不接受任何参数,因为在调用
    cleanup
    之前还存储了其他内容。@eckes,那么您正在跟踪分发的指针?如果是这样的话,那么最后两段不适用,但答案的其余部分适用。
    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);