我正在使用 Marshal.AllocHGlobal
C# 方法为 IntPtr 分配内存,然后复制 byte[] 的内容:
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?
最佳答案
Marshal.AllocHGlobal 分配的内存量超过指定的内存量并不是问题。您的代码遇到了一个不同的且很可能是致命的问题。
open_socket(char *clientIpAddr_p)
需要一个指向 C 风格 null-terminated string 的指针(也称为 NUL 终止字符串、零终止字符串或 ASCIIZ)。但是,您的 C# 代码创建一个缓冲区并用字符串字符/字节填充它,而不用空字符终止它。当在内存中(从指针给定的地址开始)搜索丢失的空字符时,这很可能会使 open_socket 函数崩溃或出现异常行为。
可能的修复方法相当简单:分配大小为 clientIpAddr.Length + 1
的缓冲区,以考虑终止空字符。必须显式设置零终止符(空字节),因为Marshal.AllocHGlobal不会用空字节初始化/填充分配的缓冲区。由于我们在这里处理的是 ASCII/ANSI 字符串,因此空字符只是一个值为 0 的字节。
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);
好吧,忘掉我刚刚写的代码吧。 ;-)
Marshal.StringToHGlobalAnsi
方法,它将直接将带有 IP 地址的 .NET 字符串转换为相应的 C 样式以 null 结尾的 ANSI 字符串。
string ipAddress = "127.0.0.1";
IntPtr client_ipAddr_p = Marshal.StringToHGlobalAnsi(ipAddress);
(除非open_socket函数需要clientIpAddr_p指向的字符串缓冲区在调用它后继续存在,否则使用Marshal.FreeHGlobal<释放分配的缓冲区
调用 native 函数后的方法,无论缓冲区是使用 Marshal.AllocHGlobal 还是 Marshal.StringToHGlobalAnsi 分配的。)
关于c# - Marshal.AllocHGlobal 正在分配比预期更多的内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56307553/