c# - pinvoke:如何释放包含 LPWSTR 的结构数组

标签 c# .net pinvoke free intptr

为了简化问题,这里是我尝试从我的 .NET 类调用的 native 方法。

[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;
    }
}

当我尝试从我的 native 代码调用这些 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()。

关于c# - pinvoke:如何释放包含 LPWSTR 的结构数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49871899/

相关文章:

c# - 异步等待按钮单击始终同步运行

c# - '>' 函数声明期间的符号?

c# - EPPlus - 从工作表中获取文件名

C# - 编写 COM 服务器 - 映射到方法的属性

c# - 发布网站并没有更新我的 CSS 包

c# - 将属性的效果级联到子类中的重写属性

c# - 新年混沌HackerRank实践题-C#解法优化

c# - 在 .NET 平台调用中使用 unsafe struct * 作为不透明类型而不是 IntPtr 是否有效?

c#调用c++ dll传输struct array发生异常

c# - 如何将 C 函数签名转换为 C# 并在 native DLL 中调用该函数?