pinvoke:如何释放包含 LPWSTR 的结构数组
pinvoke: How to free an array of structures containing LPWSTR
简化问题,这是我尝试从 .NET class.
调用的本机方法
[NativeDll.dll]
页眉
typedef struct _ADDRESS {
LPWSTR City;
} ADDRESS, *PADDRESS;
typedef struct _ADDRESS_SET {
ULONG AddressCount;
PADDRESS AddressList;
} ADDRESS_SET, *PADDRESS_SET;
DWORD WINAPI
GetAddressSet(_Outptr_ ADDRESS_SET **AddressSet);
VOID WINAPI
FreeAddressSet(__in ADDRESS_SET *AddressSet);
C++ 实现
DWORD WINAPI
GetAddressSet(_Outptr_ ADDRESS_SET **AddressSet) {
HRESULT hr = ERROR_SUCCESS;
const int totalRecords = 2;
LPCWSTR cities[totalRecords] = { L"City 1", L"City 2"};
ADDRESS *addresses = (ADDRESS*)malloc(sizeof(ADDRESS) * totalRecords);
for (int i = 0; i < totalRecords; i++) {
addresses[i].City = (wchar_t*)malloc((wcslen(cities[i]) + 1) * sizeof(wchar_t));
addresses[i].City = (LPWSTR)cities[i];
}
ADDRESS_SET *recordSet = (ADDRESS_SET*)malloc(sizeof(ADDRESS_SET));
recordSet->AddressCount = totalRecords;
recordSet->AddressList = addresses;
*AddressSet = recordSet;
return ERROR_SUCCESS;
}
VOID WINAPI
FreeAddressSet(__in ADDRESS_SET *AddressSet) {
if (AddressSet != NULL) {
if (AddressSet->AddressList != NULL) {
for (int i = 0; i < AddressSet->AddressCount; i++) {
if (AddressSet->AddressList[i].City != NULL) {
free(AddressSet->AddressList[i].City); // <-- This one AVs.
AddressSet->AddressList[i].City = NULL;
}
}
free(AddressSet->AddressList);
AddressSet->AddressList = NULL;
}
free(AddressSet);
AddressSet = NULL;
}
}
当我尝试从我的本机代码调用这些 API 时,我能够获取地址数组。但是当我尝试释放城市字符串 (LPWSTR) 时出现 AV。
这是我的 C# 代码。
[StructLayout(LayoutKind.Sequential)]
internal struct ADDRESS {
internal IntPtr City;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ADDRESS_SET {
// ULONG is 4 bytes.
// ulong in .NET is 8 bytes.
// Hence using uint.
internal UInt32 AddressCount;
internal IntPtr AddressList;
}
internal class NativeMethods {
[DllImport("nativedll.dll", EntryPoint = "GetAddressSet")]
internal static extern UInt32 GetAddressSet(ref IntPtr AddressSet);
[DllImport("nativedll.dll", EntryPoint = "FreeAddressSet")]
internal static extern UInt32 FreeAddressSet([In] IntPtr AddressSet);
}
class Program {
static void Main(string[] args) {
IntPtr pAddressSet = IntPtr.Zero;
UInt32 returnStatus = NativeMethods.GetAddressSet(ref pAddressSet);
if(returnStatus == 0 && pAddressSet != IntPtr.Zero) {
ADDRESS_SET addressSet = Marshal.PtrToStructure<ADDRESS_SET>(pAddressSet);
UInt32 addressCount = addressSet.AddressCount;
IntPtr addressList = addressSet.AddressList;
if (addressCount != 0 && addressList != IntPtr.Zero) {
for (int i = 0; i < addressCount; i++) {
ADDRESS address = Marshal.PtrToStructure<ADDRESS>(addressList);
addressList += Marshal.SizeOf(typeof(ADDRESS));
Console.WriteLine($"City: {Marshal.PtrToStringUni(address.City)}");
}
}
}
NativeMethods.FreeAddressSet(pAddressSet); // <-- Call fails to free the City string.
}
}
在这个例子中,我将地址结构中的城市字段作为 IntPtr。我也尝试将 City 字段设为 [MarshalAs(UnmanagedType.LPWStr)] 字符串,但无法成功释放。我不确定我的 .NET 代码中有什么错误。
问题出在您的 C++ 代码中。您为字符串分配了内存,但之后永远不会写入该内存。
addresses[i].City = (wchar_t*)malloc((wcslen(cities[i]) + 1) * sizeof(wchar_t));
addresses[i].City = (LPWSTR)cities[i];
如果启用编译器提示和警告,编译器会告诉您第一行中分配的值从未使用过。
第二行是错误的。相反,您必须使用 wcscpy_s() 复制字符串内容。现在 FreeAddressSet() 函数可以使用 malloc() 返回的实际指针值正确调用 free()。
简化问题,这是我尝试从 .NET class.
调用的本机方法[NativeDll.dll]
页眉
typedef struct _ADDRESS {
LPWSTR City;
} ADDRESS, *PADDRESS;
typedef struct _ADDRESS_SET {
ULONG AddressCount;
PADDRESS AddressList;
} ADDRESS_SET, *PADDRESS_SET;
DWORD WINAPI
GetAddressSet(_Outptr_ ADDRESS_SET **AddressSet);
VOID WINAPI
FreeAddressSet(__in ADDRESS_SET *AddressSet);
C++ 实现
DWORD WINAPI
GetAddressSet(_Outptr_ ADDRESS_SET **AddressSet) {
HRESULT hr = ERROR_SUCCESS;
const int totalRecords = 2;
LPCWSTR cities[totalRecords] = { L"City 1", L"City 2"};
ADDRESS *addresses = (ADDRESS*)malloc(sizeof(ADDRESS) * totalRecords);
for (int i = 0; i < totalRecords; i++) {
addresses[i].City = (wchar_t*)malloc((wcslen(cities[i]) + 1) * sizeof(wchar_t));
addresses[i].City = (LPWSTR)cities[i];
}
ADDRESS_SET *recordSet = (ADDRESS_SET*)malloc(sizeof(ADDRESS_SET));
recordSet->AddressCount = totalRecords;
recordSet->AddressList = addresses;
*AddressSet = recordSet;
return ERROR_SUCCESS;
}
VOID WINAPI
FreeAddressSet(__in ADDRESS_SET *AddressSet) {
if (AddressSet != NULL) {
if (AddressSet->AddressList != NULL) {
for (int i = 0; i < AddressSet->AddressCount; i++) {
if (AddressSet->AddressList[i].City != NULL) {
free(AddressSet->AddressList[i].City); // <-- This one AVs.
AddressSet->AddressList[i].City = NULL;
}
}
free(AddressSet->AddressList);
AddressSet->AddressList = NULL;
}
free(AddressSet);
AddressSet = NULL;
}
}
当我尝试从我的本机代码调用这些 API 时,我能够获取地址数组。但是当我尝试释放城市字符串 (LPWSTR) 时出现 AV。
这是我的 C# 代码。
[StructLayout(LayoutKind.Sequential)]
internal struct ADDRESS {
internal IntPtr City;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ADDRESS_SET {
// ULONG is 4 bytes.
// ulong in .NET is 8 bytes.
// Hence using uint.
internal UInt32 AddressCount;
internal IntPtr AddressList;
}
internal class NativeMethods {
[DllImport("nativedll.dll", EntryPoint = "GetAddressSet")]
internal static extern UInt32 GetAddressSet(ref IntPtr AddressSet);
[DllImport("nativedll.dll", EntryPoint = "FreeAddressSet")]
internal static extern UInt32 FreeAddressSet([In] IntPtr AddressSet);
}
class Program {
static void Main(string[] args) {
IntPtr pAddressSet = IntPtr.Zero;
UInt32 returnStatus = NativeMethods.GetAddressSet(ref pAddressSet);
if(returnStatus == 0 && pAddressSet != IntPtr.Zero) {
ADDRESS_SET addressSet = Marshal.PtrToStructure<ADDRESS_SET>(pAddressSet);
UInt32 addressCount = addressSet.AddressCount;
IntPtr addressList = addressSet.AddressList;
if (addressCount != 0 && addressList != IntPtr.Zero) {
for (int i = 0; i < addressCount; i++) {
ADDRESS address = Marshal.PtrToStructure<ADDRESS>(addressList);
addressList += Marshal.SizeOf(typeof(ADDRESS));
Console.WriteLine($"City: {Marshal.PtrToStringUni(address.City)}");
}
}
}
NativeMethods.FreeAddressSet(pAddressSet); // <-- Call fails to free the City string.
}
}
在这个例子中,我将地址结构中的城市字段作为 IntPtr。我也尝试将 City 字段设为 [MarshalAs(UnmanagedType.LPWStr)] 字符串,但无法成功释放。我不确定我的 .NET 代码中有什么错误。
问题出在您的 C++ 代码中。您为字符串分配了内存,但之后永远不会写入该内存。
addresses[i].City = (wchar_t*)malloc((wcslen(cities[i]) + 1) * sizeof(wchar_t));
addresses[i].City = (LPWSTR)cities[i];
如果启用编译器提示和警告,编译器会告诉您第一行中分配的值从未使用过。
第二行是错误的。相反,您必须使用 wcscpy_s() 复制字符串内容。现在 FreeAddressSet() 函数可以使用 malloc() 返回的实际指针值正确调用 free()。