我在c#中创建非托管内存块,并用结构中的数据填充它。
我遍历结构列表并执行以下操作:
var st = context.myTable.Select(x => x.SomeProperty).ToList();
struct包含引用类型:“string”。 我已经研究了BOL的StructureToPtr方法,然后说:
Marshal.StructureToPtr(structTemp, currentMemoryPosition, false);
currentMemPosition = new IntPtr(currentMemPosition.ToInt64() + structSize);
究竟是什么意思?
这是否意味着对该字符串的引用仍将在内存中,尽管结构的实例将超出范围?
上面的非托管内存块,我传递给使用它的c ++方法。当在c ++部分上完成作业时,我再次遍历内存中的结构(在c#中)和:
"All other reference types (for example, strings and arrays) are marshaled to copies"
对我来说最重要的问题是:
Marshal.DestroyStructure(currentMemPosition, typeof(struct));
字符串引用类型的结构布局是:
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)
答案 0 :(得分:4)
"所有其他引用类型(例如,字符串和数组)都被封送到副本" 究竟是什么意思?
Marshal.StructureToPtr
创建string
的副本。即使你正在编组LPWStr
,它也会这样做。这与将参数传递给方法不同,有时字符串/数组不会被复制,而是直接传递。
因此,在调用Marshal.StructureToPtr
之后,您现在拥有iuTest
的两个副本:变量iuTest
中的一个副本,由.NET直接管理(因此将自动解除分配) ,以及Marshal.StructureToPtr
创建的副本中的一个。必须手动销毁此副本,例如使用Marshal.DestroyStructure
。
请注意,此处忽略SizeConst = 36
,因为所需的确切内存量将由Marshal.StructureToPtr
分配。
完整示例:
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);
然后:
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);
和C ++(CoTaskMem*
)位于#include <Objbase.h>
):
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!
}
}
结果:
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# - 你甚至可以使用封送结构的char*
指针...你知道它在编组结构的偏移0处(因为它是第一个字段),所以:
IntPtr ptr2 = Marshal.ReadIntPtr(ptr, 0); // will read the char* pointer
string str2 = Marshal.PtrToStringAnsi(ptr2);
Console.WriteLine("Unmarshaling manually: {0}", str2);
(没有直接连接到问题,在char中询问):
将数组从C#编组到C ++,从C ++编组回C#另一个数组:
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);