我在 C# 中创建非托管内存块并用来自结构的数据填充它。
我遍历结构列表并执行如下操作:
Marshal.StructureToPtr(structTemp, currentMemoryPosition, false);
currentMemPosition = new IntPtr(currentMemPosition.ToInt64() + structSize);
该结构包含引用类型:“字符串”。 我已经研究了 BOL 的 StructureToPtr 方法,它说:
"All other reference types (for example, strings and arrays) are marshaled to copies"
这到底是什么意思?
这是否意味着尽管结构实例将超出范围,但对该字符串的引用仍将保留在内存中?
上面的非托管内存块,我传递给使用它的 c++ 方法。在 C++ 部分完成工作后,我再次遍历内存中的结构(在 C# 中)和:
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)
字符串引用类型的结构布局是:
[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");
}
}
最佳答案
"All other reference types (for example, strings and arrays) are marshaled to copies" What exactly does it mean?
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);
(与问题没有直接关系,以字符形式提问):
将数组从 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);
关于c# - 将字符串编码到非托管内存,将它们传递给 C++,然后再次返回给 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30995780/