我正在使用Marshal.AllocHGlobal
c#方法为IntPtr分配内存,然后复制包含的字节[]:
byte[] clientIpAddr = System.Text.Encoding.ASCII.GetBytes("127.0.0.1")
IntPtr client_ipAddr_p = IntPtr.Zero;
ipAddress_p = Marshal.AllocHGlobal(clientIpAddr.Length);
Marshal.Copy(clientIpAddr, 0, ipAddress_p, clientIpAddr.Length);
然后,我将指针传递给C ++外部库,该库接收我的变量,如下所示:
int open_socket(char *clientIpAddr_p)
{
<open a socket>
}
根据AllocHGlobal
的定义,它最终可以保留比预期更多的内存(我可以保证,因为我现在正遭受痛苦)。问题是我需要IntPtr
来具有我所要求的大小,因为以后需要精确的IP来打开套接字。
是否有更好的分配外部内存的方法来确保char *
拥有我的正确IP?
答案 0 :(得分:4)
Marshal.AllocHGlobal 分配比指定数量更多的内存不是问题。您的代码遭受另一个不同且很可能致命的问题。
open_socket(char *clientIpAddr_p)
需要一个指向C样式null-terminated string(也称为NUL终止的字符串,零终止的字符串或ASCIIZ)的指针。但是,您的C#代码创建一个缓冲区,并用字符串字符/字节填充该缓冲区,而不会以空字符结尾。在内存(从指针给定的地址开始)中搜索缺少的空字符时,这很可能会使 open_socket 函数崩溃或出现其他异常情况。
可能的解决方法非常简单:为缓冲区分配clientIpAddr.Length + 1
,以解决空字符的终止问题。必须明确设置零终止符(一个空字节),因为 Marshal.AllocHGlobal 不会 not 初始化/用空字节填充分配的缓冲区。由于我们正在处理ASCII / ANSI字符串,因此空字符只是一个值为零的字节。
byte[] clientIpAddr = System.Text.Encoding.ASCII.GetBytes("127.0.0.1")
IntPtr client_ipAddr_p = Marshal.AllocHGlobal(clientIpAddr.Length + 1);
Marshal.Copy(clientIpAddr, 0, ipAddress_p, clientIpAddr.Length);
Marshal.WriteByte(client_ipAddr_p, clientIpAddr.Length, 0);
好的,忘记我刚刚编写的代码。 ;-)
为什么?因为有一个甚至更简单的“一站式”解决方案,所以省去了手动分配和填充C样式以null终止的字符串的缓冲区的麻烦:Marshal.StringToHGlobalAnsi
方法将转换您的代码。带有IP地址的.NET字符串直接转换为对应的C样式的以N结尾的ANSI字符串。
string ipAddress = "127.0.0.1";
IntPtr client_ipAddr_p = Marshal.StringToHGlobalAnsi(ipAddress);
(除非 open_socket 函数要求 clientIpAddr_p 指向的字符串缓冲区在调用后继续存在,否则在之后使用Marshal.FreeHGlobal
方法释放分配的缓冲区不管是否使用 Marshal.AllocHGlobal 或 Marshal.StringToHGlobalAnsi 分配了缓冲区,都将调用本机函数。)