代码之家  ›  专栏  ›  技术社区  ›  Green Apple

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

  •  2
  • Green Apple  · 技术社区  · 6 年前

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

    [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时,我能够获得地址数组。但是AV当我尝试释放城市字符串(LPWSTR)时。

    这是我的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.
        }
    }
    

    在本例中,地址结构中的City字段为IntPtr。我也尝试将City字段设置为[marshallas(UnmanagedType.LPWStr)]字符串,但未能成功释放。我不确定我的错误是什么。NET代码。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Hans Passant    6 年前

    问题出在您的C++代码中。为字符串分配内存,但从不写入该内存。

    addresses[i].City = (wchar_t*)malloc((wcslen(cities[i]) + 1) * sizeof(wchar_t));
    addresses[i].City = (LPWSTR)cities[i];
    

    如果启用了编译器提示和警告,编译器将告诉您第一行中指定的值永远不会被使用。

    第二行是错误的。相反,您必须使用wcscpy\u s()复制字符串内容。现在,FreeAddressSet()函数可以使用malloc()返回的实际指针值正确调用free()。