2015-06-23 11 views
5

C# 'de yönetilmeyen bellek bloğu oluşturuyorum ve yapılardan veri ile dolduruyorum.Yönetilmeyen bellekte dizginleme dizeleri, C++ 'ya geçiriliyor ve tekrar C#' ye geri dönüyor.

Ben yapılar listesinde yineleme ve böyle bir şey yapmak: "dize":

Marshal.StructureToPtr(structTemp, currentMemoryPosition, false); 
currentMemPosition = new IntPtr(currentMemPosition.ToInt64() + structSize);  

yapı referans türü içerir. Bir StructureToPtr yöntemi için BOL inceledik ve orada diyor:

"All other reference types (for example, strings and arrays) are marshaled to copies" 

tam olarak ne anlama geliyor?

Bu, bir yapının örneğinin kapsam dışı kalacağına rağmen, bu dizeye yapılan başvurunun hala bellekte olacağı anlamına mı geliyor?

Yukarıdaki yönetilmeyen bellek bloğu, bunu kullanan C++ yöntemine geçiyorum. iş C++ kısmında bitince (C#) bellekte yapılar üzerinden tekrar yineleme ve:

Marshal.DestroyStructure(currentMemPosition, typeof(struct)); 

benim için en önemli soru şudur:

Whether I can: 

1) Create structs with strings inside 
2) Marshal them to unmanaged mamory 
3) Make use of them on c++ side 
4) **Return them from c++** 
5) Read them again in c# 
6) Deallocate them in c# by using Marshal.DestroyStructure (EDITED) 

dize referans türü ile yapı düzeni geçerli:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack = 1), Serializable] 
internal struct TempStruct 
{ 
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 36)] 
    private string iuTest; 

    public TempStruct(Guid IuTest) 
     : this() 
    { 
     iuTest = IuTest.ToString("D"); 
    } 
} 
+0

siz "geri kopya" değişiklikleri Marshal.StructureToPtr 'yarattığı yapının kopyasına yapılan için' Marshal.PtrToStructure' kullanmak zorunda C# tekrar okumak için '. 6. nokta yanlış: Kopyayı "Marshal.DestroyStructure" ile ayırmanız gerekiyor. Marshal.PtrToStructure' – xanatos

+0

için tahsis ettiğiniz belleği boşaltmanız gerektiğini unutmayın, C++ tarafındaki dizeleri değiştirmem. C++ 5 dizelerine geçtiğimi ve C++ 'nın 2 değerini döndürdüğümü söyleyelim (onlara dokunmadan) – John

+0

Onları nasıl iade edersiniz? – xanatos

cevap

4

"(örnek, dizeleri ve diziler için) Bütün diğer referans türleri kopyalarına konmasını olan" tam olarak ne i yapar demek istedim

Marshal.StructureToPtr, string'un kopyasını oluşturur. LPWStr'a kük olsa bile bunu yapar. Bu, parametrelerin, bazen dizelerin/dizilerin kopyalanmadığı, doğrudan aktarıldığı yöntemlere aktarılmasından farklıdır. Değişken iuTest bir tane, o doğrudan .NET tarafından yönetilmektedir (ve böylece otomatik olarak ayırmanın edilecektir) ve Marshal.StructureToPtr yarattığı kopyada: biri

Yani Marshal.StructureToPtr çağırdıktan sonra artık iki senin iuTest kopyaları var. Bu kopya manuel olarak imha edilmelidir, örneğin Marshal.DestroyStructure ile.

Gerekli bellek miktarının Marshal.StructureToPtr tarafından ayrılacağı için burada SizeConst = 36 öğesinin göz ardı edildiğini unutmayın.

Tam örnek: o

C#

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1), Serializable] 
internal struct TempStruct 
{ 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string iuTest; 

    public TempStruct(Guid IuTest) 
     : this() 
    { 
     iuTest = IuTest.ToString("D"); 
    } 
} 

[DllImport("NativeLibrary", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
public static extern void TempStructMethod(IntPtr ptr); 

ve:

var str = new TempStruct(Guid.NewGuid()); 

Console.WriteLine("C# side: {0}", str.iuTest); 

// It will be 4 or 8, depending if you are running 32 or 64 bits 
int size = Marshal.SizeOf(typeof(TempStruct)); 

IntPtr ptr = Marshal.AllocCoTaskMem(size); 

// Marshaling to ptr 
Marshal.StructureToPtr(str, ptr, false); 

// Call your C++ method 
TempStructMethod(ptr); 

Console.WriteLine("C# side in original struct: {0}", str.iuTest); 

// Marshaling back 
str = (TempStruct)Marshal.PtrToStructure(ptr, typeof(TempStruct)); 

Console.WriteLine("C# side after marshaled back: {0}", str.iuTest); 

// Freeing the "content" of the marshaled struct (the marshaled 
// string in this case) 
Marshal.DestroyStructure(ptr, typeof(TempStruct)); 

// Freeing the memory allocated for the struct object (the 
// "container") 
Marshal.FreeCoTaskMem(ptr); 

ve C++ (CoTaskMem*#include <Objbase.h> olarak ifade edilmiştir):

extern "C" 
{ 
    __declspec(dllexport) void TempStructMethod(TempStruct *ts) 
    { 
     printf("C++ side: %s\n", ts->iuTest); 

     // If you want to free a C# marshaled string use CoTaskMemFree 
     // See in https://github.com/dotnet/coreclr/blob/4cf8a6b082d9bb1789facd996d8265d3908757b2/src/vm/fieldmarshaler.cpp 
     // FieldMarshaler_StringAnsi::DestroyNativeImpl and 
     // FieldMarshaler_StringUni::DestroyNativeImpl 

     // Here we want to modify the string C-side 

     // First we free it 
     CoTaskMemFree(ts->iuTest); 
     ts->iuTest = NULL; 

     char *str = "abcdefab-cdef-abcd-efab-cdefabcdefab"; 
     int len = strlen(str) + 1; 

     // Then we allocate a new string 
     // Use CoTaskMemAlloc to allocate memory that can be freed by C# 
     ts->iuTest = (char*)CoTaskMemAlloc(len); 

     // Then we copy our string in the allocated memory 
     strcpy(ts->iuTest, str); 

     // Note that you could have reused the "original" 
     // memory of ts->iuTest in this case, because it 
     // was of the "right" size. I freed and reallocated 
     // just to show how to do it! 
    } 
} 

R esult:

C# side: d3ccb13c-fdf9-4f3d-9239-8d347c18993c 
C++ side: d3ccb13c-fdf9-4f3d-9239-8d347c18993c 
C# side in original struct: d3ccb13c-fdf9-4f3d-9239-8d347c18993c 
C# side after marshaled back: abcdefab-cdef-abcd-efab-cdefabcdefab 

C# hatta konmasını yapının char* işaretçisi kullanabilirsiniz -side ... Sen (ilk alan olduğu için) konmasını yapının ofset 0 azından olduğunu biliyoruz, bu yüzden:

IntPtr ptr2 = Marshal.ReadIntPtr(ptr, 0); // will read the char* pointer 
string str2 = Marshal.PtrToStringAnsi(ptr2); 
Console.WriteLine("Unmarshaling manually: {0}", str2); 

(doğrudan char sorulan soruya, bağlı değil):

: sırt C++ C# başka diziye sıralanırken,

C++ C# gelen bir dizi marshalling 10 C++

struct TempStruct 
{ 
    char* iuTest; 
}; 

extern "C" 
{ 
    __declspec(dllexport) void GetSomeData(TempStruct *inPtr, TempStruct **outPtr, int *numPtr) 
    { 
     // Number of elements we want to return 
     *numPtr = 10; 

     // Space for these elements 
     *outPtr = (TempStruct*)malloc(*numPtr * sizeof(TempStruct)); 

     for (int i = 0; i < *numPtr; i++) 
     { 
      TempStruct *curr = *outPtr + i; 

      // Here we allocate some space for the char* iuTest 
      curr->iuTest = (char*)malloc(10); 

      // And we write something on it (in this case the 'i') 
      itoa(i, curr->iuTest, 10); 
     } 
    } 

    __declspec(dllexport) void FreeSomeData(TempStruct *ptr, int num) 
    { 
     for (int i = 0; i < num; i++) 
     { 
      TempStruct *curr = ptr + i; 

      // First we free the char* iuTest 
      free(curr->iuTest); 
     } 

     // Then we free the ptr 
     free(ptr); 
    } 
} 

C#

// Some TempStruct(s) to pass to C++ 
TempStruct[] someData = new TempStruct[5]; 

for (int i = 0; i < someData.Length; i++) 
{ 
    someData[i] = new TempStruct(Guid.NewGuid()); 
} 

// C++ will return its TempStruct array in ptr 
IntPtr ptr; 
int length; 

GetSomeData(someData, out ptr, out length); 

// This must be equal to C++ sizeof(TempStruct) 
int size = Marshal.SizeOf(typeof(TempStruct)); 

TempStruct[] someData2 = new TempStruct[length]; 

for (int i = 0; i < length; i++) 
{ 
    // We marshal back an element of TempStruct 
    IntPtr curr = (IntPtr)(ptr + (size * i)); 
    someData2[i] = (TempStruct)Marshal.PtrToStructure(curr, typeof(TempStruct)); 
} 

// Important! We free the TempStruct allocated by C++. We let the 
// C++ do it, because it knows how to do it. 
FreeSomeData(ptr, length); 
+0

SizeConst göz ardı edilmiyor - dizge 'MarshalAs' özniteliğinde belirtilen bir boyut sabitine sahip değilse, 'StructureToPtr' çağrısı başarısız olur, bu etrafta çalışmaz bir CLR gereksinimidir. – aevitas

+0

@aevitas Hayır, yapmıyorsunuz. Bir 'UnmanagedType.ByValTStr' yapmak istiyorsanız, buna ihtiyacınız var. Ve göz ardı edilir.'Debug-> Windows-> Bellek ' – xanatos

+0

ile oluşturulan işaretçiyi izledim. Bu göz ardı edilebilir ama CLR tarafından yapı için anlamlı bir boyut elde edebilmek için gereklidir. CLR'nin yapılarla ilgili temel ilkeleri. – aevitas