将字符串数组从c#传递到内存管理

Passing array of strings from c# to memory management

本文关键字:内存 管理 字符串 数组      更新时间:2023-10-16

在我的代码中,我有c DLL接受字符串数组:void Helper::ProcessEvent(PEVENT_RECORD pEvent,wchar_t** OutPutFormattedData)

我用以下代码调用它:

[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
    internal static extern uint ProcessEvent(
        [In, Out] 
        ref EVENT_RECORD pEvent,
        [In, Out] 
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] ref string[] pResult);

在c++代码中,这里是我用来填充数组的主要代码:

for(int i=0;i<list.Size;i++)
{
    EventWrapperClass *EWC = new EventWrapperClass();
    EWC = list.Events[i];
    OutPutFormattedData[i] = new wchar_t [wcslen(HelperFormatMessage(L"%s: %sn",EWC->GetProperyName(),EWC->GetProperyValue()))+1];
    wcscpy(OutPutFormattedData[i] ,HelperFormatMessage(L"%s: %sn",EWC->GetProperyName(),EWC->GetProperyValue()));

}

和调用代码:

string[] strArr= new string[1];
NativeHelper.ProcessEvent(ref eventRecord, ref strArr);

我有两个问题:

  1. 为什么当我在c#中调用这个函数后检查传入数组的值时,我看到它是空的(数据存在于c++代码中,我调试了它)?

  2. 如果我在c++ dll中分配内存,我需要在哪里释放它?用c++还是c#?

多谢!

编辑:

c++的

签名:

static __declspec(dllexport) void ProcessEvent(PEVENT_RECORD pEvent, wchar_t** OutPutFormattedData);

如果您在c#中分配(托管或非托管)缓冲区(或数组或字符),然后填充c++:
1. c#中的Deallocate .
2. 在c#签名中,Buffer应该是[in]而不是ref/out。

如果你在c++中分配一个缓冲区(或字符数组):
1. 将buffer作为[out]传入IntPtr。
2. 在c++签名中添加deallocate方法
3.将缓冲区复制到c#缓冲区或新建字符串
4.

在c#中使用IntPtr调用c++ deallocater。

你可以使用System.Runtime.InteropServices.Marshal在c#中分配/释放非托管内存。不要使用此命令释放在。net外部分配的内存!

System.Runtime.InteropServices。Marshal还可以用于将内存从外部缓冲区(IntPtr)复制到。net缓冲区或字符串。

将导入方法标记为private,并使用。net参数(例如使用System.Runtime.InteropServices.Marshal.Copy并调用外部deallocate)的公共(或受保护或内部)方法包装。

c++:

int ProcessEvent(PEVENT_RECORD eventData, wchar_t*& message)
{
    // example code
    message = new wchar_t[100];
    if (message == 0)
        return 1;
}
void DeallocateString(wchar_t* array)
{
    delete[] arrayPtr;
}
wchar_t* ErrorCodeToMessage(int errorCode)
{
    switch (errorCode)
    {
        case 0: return 0; // return NULL pointer
        case 1: return L"No!!!";
        default: return L"WTF!?";
    }
}
c#:

[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
private static extern uint ProcessEventExternal(
    [In, Out] ref EventData eventData,
    [In, Out, MarshalAs(UnmanagedType.SysInt))] ref IntPtr resultMessages);
[DllImport("Helper.dll", EntryPoint = "DeallocateString")]
private static extern voidDeallocateStringExternal(
    [In, MarshalAs(UnmanagedType.SysInt)] IntPtr arrayPtr);
[DllImport("Helper.dll", EntryPoint = "ErrorCodeToMessage")]
private static extern
    [return: MarshalAs(UnmanagedType.SysInt)] IntPtr
    ErrorCodeToMessageExternal(int errorCode);
public string ProcessEvent(ref EventData eventData)
{
    IntPtr resultPtr = IntPtr.Zero;
    uint errorCode = ProcessEventExternal(eventData, ref resultPtr);
    if (errorCode != null)
    {
        var errorPtr = ErrorCodeToMessageExternal(errorCode);
        // returns constant string - no need to deallocate
        var errorMessage = Marshal.PtrToStringUni(errorPtr);
        throw new ApplicationException(errorMessage);
    }
    var result = Marshal.PtrToStringUni(resultPtr);
    ExternalDeallocate(resultPtr);
    return result;
}

我不知道问题1,但关于问题2:

for(int i=0;i<list.Size;i++) 
{
    EventWrapperClass *EWC = new EventWrapperClass();
    EWC = list.Events[i]; 
    ...
}

在第一行,在循环中,您创建了一个新对象。在第二行中,为指针分配一个不同的对象,该对象存储在list.Events[i]中。此时,不再有任何对象指向您的第一个对象,并且每次循环迭代都有内存泄漏!

将这两行改为

EventWrapperClass *EWC = list.Events[i];

,因为不需要创建新对象。

然后,如果您不再需要该事件,则在循环中删除它,并确保随后清除列表。只有当您100%确定不再需要该事件时,才可以执行此操作。这很容易产生悬空指针!