简体   繁体   English

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

[英]pinvoke: How to free an array of structures containing LPWSTR

Simplifying the problem, here is the native methods that I try to invoke from my .NET class. 为简化问题,这是我尝试从.NET类调用的本机方法。

[NativeDll.dll] [NativeDll.dll]

Header

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++ Implementation 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;
    }
}

When i try to invoke these APIs from my native code, I am able to get the array of addresses. 当我尝试从本机代码调用这些API时,我能够获取地址数组。 But AV when I try to free the City string (LPWSTR). 但是,当我尝试释放城市字符串(LPWSTR)时出现AV。

Here is my C# code. 这是我的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.
    }
}

In this example i have the City field inside the Address structure as IntPtr. 在此示例中,我将Address结构内的City字段设置为IntPtr。 I have tried the City field to be [MarshalAs(UnmanagedType.LPWStr)] string as well but couldn't get to free successfully. 我也尝试将City字段设置为[MarshalAs(UnmanagedType.LPWStr)]字符串,但无法成功释放。 I am not sure what is the mistake in my .NET code. 我不确定.NET代码中有什么错误。

The problem is in your C++ code. 问题出在您的C ++代码中。 You allocate memory for the string, but then never write to that memory. 您为该字符串分配内存,但是永远不要写入该内存。

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

If you enabled compiler hints and warnings the compiler will tell you that the value assigned in the first line is never used. 如果启用了编译器提示和警告,编译器将告诉您第一行中分配的值将永远不会使用。

That second line is wrong. 第二行是错误的。 Instead you must copy the string content with wcscpy_s(). 相反,您必须使用wcscpy_s()复制字符串内容。 Now the FreeAddressSet() function can properly call free(), using the actual pointer value returned by malloc(). 现在,FreeAddressSet()函数可以使用malloc()返回的实际指针值正确调用free()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM