2011-09-03 6 views
6

Desidero allocare e deallocare la memoria nel codice non gestito (C++) e le chiamiamo funzioni dal codice gestito (C#). Non so se il seguente codice va bene senza perdite di memoria o no?Assegnazione e deallocazione della memoria nel codice non gestito tramite la piattaforma Invoke (C#)

codice C#:

[DllImport("SampleDLL.dll")] 
public extern void getString([MarshalAs(UnmanagedType.LPStr)] out String strbuilder); 

[DllImport("SampleDLL.dll")] 
public extern void freeMemory([MarshalAs(UnmanagedType.LPStr)] out String strBuilder); 

.... 

//call to unmanaged code 
getString(out str); 
Console.WriteLine(str); 
freeMemory(out str); 

codice C++:

extern void _cdecl getString(char **str) 
{ 
    *str = new char[20]; 
    std::string temp = "Hello world"; 
    strncpy(*str,temp.c_str(),temp.length()+1); 
} 

extern void _cdecl freeMemory(char **str) 
{ 
    if(*str) 
     delete []*str; 
    *str=NULL; 
} 

risposta

4

No, questo non può funzionare. Il marshaller pinvoke sta per provare a rilasciare la memoria per la stringa con CoTaskMemFree(). Non sa altrimenti che hai una funzione di rilascio. Non funzionerà bene, non hai allocato la stringa con CoTaskMemAlloc. Questa sarà una perdita di memoria silenziosa in XP, un crash in Vista e in alto.

si deve fermare il marshaller dal provare a fare il lavoro giusto:

[DllImport("SampleDLL.dll")] 
public extern void getString(out IntPtr strptr); 

[DllImport("SampleDLL.dll")] 
public extern void freeMemory(IntPtr strptr); 

Che poi richiede Marshal.PtrToStringAnsi() nel codice C# per Maresciallo la stringa se stessi dal puntatore restituito.

+0

Ciao, perché non hai usato la parola chiave 'out' per il parametro nella funzione freeMemory? – user186246

+0

Perché si chiama CoTaskMemFree()? cosa fa in realtà? – user186246

+0

Perché freeMemory non restituisce un puntatore. CoTaskMemAlloc() è l'allocatore di memoria per COM. Se non c'è modo di indovinare quale heap è stato utilizzato, allora il marshaller indovina l'heap COM. –

0

Personalmente ritengo che ciò avvenga più facilmente utilizzando uno BSTR evitando così la necessità di esportare un deallocator.

C++

BSTR ANSItoBSTR(const char* input) 
{ 
    BSTR result = NULL; 
    int lenA = lstrlenA(input); 
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0); 
    if (lenW > 0) 
    { 
     result = ::SysAllocStringLen(0, lenW); 
     ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW); 
    } 
    return result; 
} 

BSTR __stdcall getString() 
{ 
    return ANSItoBSTR("Hello world"); 
} 

Naturalmente, se si sta lavorando con Unicode stringhe la sua ancora più facile.

BSTR __stdcall getString() 
{ 
    return ::SysAllocString(L"Hello world"); 
} 

C#

[DllImport(@"test.dll")] 
[return: MarshalAs(UnmanagedType.BStr)] 
private static extern string getString(); 

E sul lato C# il gioco è fatto. Basta chiamare lo getString() e restituisce uno .net string e non è necessario eseguire il marshalling di alcunché o chiamare un deallocator.