繁体   English   中英

我无法从C#获取PInvoke到C ++ DLL的输入参数,以用作IntPtr的输出

[英]I can't get the input parameter of a PInvoke to C++ DLL from C# to be used as ouput with IntPtr

我在C ++ DLL中有一个函数,需要一个输入。 我正在尝试将该输入用作C#调用的输出。

这是我的C ++函数:

MYAPI int testStuff3(unsigned char* str)
{
    printf("%s\n", str);
    str = (unsigned char*)malloc(9);
    str[0] = 'G';
    str[1] = 'o';
    str[2] = 'o';
    str[3] = 'd';
    str[4] = 'b';
    str[5] = 'y';
    str[6] = 'e';
    str[7] = '!';
    str[8] = '\0';
    return 1;
}

这是C#代码:

public class Program
{
    [DllImport("NativeLib.dll")]
    private static extern int testStuff3([In, Out] IntPtr str);

    static void Main(string[] args)
    {
        IntPtr junk3 = IntPtr.Zero;
        int ret = testStuff3(junk3);
        Byte[] stuff3 = new byte[9];
        Marshal.Copy(junk3, stuff3, 0, 9);
    }
}

调用Marshal.Copy时,会出现错误,指出来源(junk3)不能为null。

从C#发送指向C ++ DLL的空指针并让DLL分配内存并在其中存储某些内容并将其返回给调用者,这将行不通吗? 我想将其保留为IntPtr而不是StringBuilder,因为数据在最终代码中不一定是字符串。 只是C ++中的无符号char数组,我希望IntPtr指向它。

我为IntPtr传递尝试了[In,Out],[Out],out和ref的不同变体。

永远不要让内存分配跨越DLL边界。 那就是疯狂和/或斯巴达。

(对于专家而言:您可以分配内存,然后再传递一个指针,只要您将所有权传回以释放它,或保证将同一分配器用作合同的一部分即可。但是在可能的情况下,还是要避免)

通常,要使用字符串输出参数,应传递StringBuilder作为参数,并将其容量设置为最大预期长度。 然后,在本机代码中,您只需填充此现有缓冲区。

有关示例,请参见此处的“固定长度字符串缓冲区”部分。

您的C ++函数不会修改传递的字符串。 它使用malloc分配一个新变量,将其存储在忘记传递值的本地变量中,然后返回泄漏内存。

如果出于某种原因要进行手动编组,则可能需要这样的操作(假设这是针对Windows的):

MYAPI BOOL __stdcall testStuff3( char** pp )
{
    if( nullptr == pp )
        return FALSE;   // null pointer
    if( nullptr != *pp )
    {   // Print & release an old string
        printf( "%s\n", *pp );
        CoTaskMemFree( *pp );
        *pp = nullptr;
    }
    // Allocate a new one
    const char* const str = CoTaskMemAlloc( 9 );
    if( nullptr == str ) return FALSE;
    strncpy( str, "Goodbye!", 9 );
    *pp = str;
    return TRUE;
}

C#:

public class Program
{
    [DllImport( "NativeLib.dll" )]
    private static extern bool testStuff3( [In, Out] ref IntPtr str );

    static void Main( string[] args )
    {
        IntPtr ptr = IntPtr.Zero;
        if( testStuff3( ref ptr ) )
        {
            Console.WriteLine( Marshal.PtrToStringAnsi( ptr ) );
            Marshal.FreeCoTaskMem( ptr );
        }
    }
}

但是,除非您有充分的理由,否则我不建议您这样做。 在大多数情况下,自动编组会更好。 对于C#-> C ++,它非常简单,C ++中的const char*const wchar_t* ,C#中的string (具有正确的属性)。 对于C ++-> C#,您可以在C#中分配StringBuilder,将char*wchar_t*传递给C ++,并在另一个参数中缓冲长度。

谢谢您的帮助!

这就是我最后得到的。

C ++函数:

MYAPI int testStuff4(wchar_t* str)
{
    unsigned char* stuff = (unsigned char*)malloc(10);
    stuff[0] = 'G';
    stuff[1] = 'o';
    stuff[2] = 'o';
    stuff[3] = 'd';
    stuff[4] = 'b';
    stuff[5] = 'y';
    stuff[6] = 'e';
    stuff[7] = '!';
    stuff[8] = '\0';

    mbstowcs(str, (const char*)stuff, 1024);
    free(stuff);

    return 1;
}

C#函数:

public class Program
{
    [DllImport("NativeLib.dll")]
    private static extern int testStuff4(IntPtr str);

    static void Main(string[] args)
    {
        IntPtr junk4 = Marshal.AllocHGlobal(1024);
        int ret = testStuff4(junk4);
        string junkString = Marshal.PtrToStringUni(junk4);
        Console.WriteLine(junkString);
        Marshal.FreeHGlobal(junk4);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM