在我的代码中,我有一个接受字符串数组的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: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()))+1];
wcscpy(OutPutFormattedData[i] ,HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()));
}
调用代码:
string[] strArr= new string[1];
NativeHelper.ProcessEvent(ref eventRecord, ref strArr);
我有两个问题:
为什么在调用此函数后检查c#中传递的数组的值时,我看到它是空的(数据存在于c ++代码中,我对其进行了调试)?
如果我在c ++ dll中分配内存,我需要释放它吗?在c ++或c#?
非常感谢!
编辑:
c ++的结构:
static __declspec(dllexport) void ProcessEvent(PEVENT_RECORD pEvent, wchar_t** OutPutFormattedData);
答案 0 :(得分:1)
如果在C#中分配(托管或非托管)缓冲区(或数组或字符),然后填写C ++:
1.在C#中解除分配
2.缓冲区应该在[in]中并且没有ref或out在C#签名中
如果在C ++中分配缓冲区(或字符数组):
1.将缓冲区作为[out]输出IntPtr。
2.将deallocate方法添加到C ++签名中
3.将缓冲区复制到C#缓冲区或新字符串
4.使用IntPtr从C#调用C ++ deallocater。
您可以使用System.Runtime.InteropServices.Marshal在C#中分配/取消分配未管理的内存。不要用来释放在.NET之外分配的内存!
System.Runtime.InteropServices.Marshal也可用于将内存从外部缓冲区(IntPtr)复制到.NET缓冲区或字符串。
将导入的方法标记为私有,并使用.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 :(得分:0)
我不知道问题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%确定不再需要该事件,则只能执行此操作。这很容易产生悬空指针!