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



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

这是我的C++函数:

MYAPI int testStuff3(unsigned char* str)
{
printf("%sn", 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] = '';
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( 不能为空。

这是否不起作用,从 C# 发送指向 DLL C++空指针并让 DLL 分配内存并在其中存储一些东西并将其返回给调用方? 我想让它保持 IntPtr 而不是 StringBuilder,因为数据不一定是最终代码中的字符串。 只是一个无符号的字符数组C++,我希望 IntPtr 指向它。

我已经尝试了 [In、Out]、[Out]、out 和 ref 的不同变体,用于 IntPtr 传递。

永远不要允许内存分配跨越 DLL 边界。 那条路是疯狂的,和/或斯巴达。

(对于迂腐的人:你可以分配内存,然后传递一个指针,只要你要么把所有权传回去释放它,要么保证使用相同的分配器作为合约的一部分。 但在可能的情况下,这仍然是要避免的事情。

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

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

感谢您的帮助!

这是我最终得到的。

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] = '';
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);
}
}

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( "%sn", *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++,并在另一个参数中传递缓冲区长度。

最新更新