1

In my code I have c DLL that accepts array of strings: void Helper::ProcessEvent(PEVENT_RECORD pEvent,wchar_t** OutPutFormattedData)

I'm invoking it with this code:

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

In c++ code here is the main code I'm using to fill the array:

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: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()))+1];
    wcscpy(OutPutFormattedData[i] ,HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()));

}

And the invoke code:

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

I have two questions:

  1. Why when I check the value of the passed array in c# after calling this function I see it's empty (data is exist in c++ code, I debugged it) ?

  2. If I'm allocating memory in c++ dll, where I need to free it? in c++ or c#?

Many thanks!

Edit:

signture of c++:

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

2 Answers 2

1

If you allocate a (managed or unmanaged) buffer (or array or chars) in C# and then fill in C++:
1. Deallocate in C#.
2. Buffer should be [in] and not have ref or out in C# signature.

If you allocate a buffer (or array of chars) in C++:
1. Pass buffer as [out] out IntPtr.
2. Add deallocate method to C++ signature.
3. Copy buffer into C# buffer or new string
4. Call C++ deallocater from C# with IntPtr.

You can use System.Runtime.InteropServices.Marshal to allocate/deallocate unmanged memory within C#. Do not use to deallocate memory allocated outside .NET!

System.Runtime.InteropServices.Marshal can also be used to copy memory from external buffer (IntPtr) to .NET buffer or string.

Mark imported method as private and wrap with a public (or protected or internal) method that uses the .NET parameters (e.g. that uses System.Runtime.InteropServices.Marshal.Copy and calls external 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;
}
Sign up to request clarification or add additional context in comments.

2 Comments

@Moti, What is the signature of the C++ method - edit question to add. Do you have a DllExport on it?
here is the signture of c++: static __declspec(dllexport) void ProcessEvent(PEVENT_RECORD pEvent,wchar_t** OutPutFormattedData);
0

I don't know about question 1, but about question 2:

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

In the first line, within the loop, you create a new object. In the second line, you assign a different object to your pointer, which is the one stored in list.Events[i]. At this point, nothing points to your first object any longer, and you have a memory leak for every iteration of the loop!

Change the two lines to

EventWrapperClass *EWC = list.Events[i];

as there is no need to create a new object.

Then, if you don't need the event any longer, delete it within the loop and be sure to clear the list afterwards. You may only do this if you are 100% sure the event is not needed any longer. This can easily produce dangling pointers!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.