将 c++ 函数包装为 c#,具有结构的总动态大小
Wrapping c++ function to c# with total dynamic size of struct
我正在努力将C++函数包装为 C#。 我对这种包装有非常基本的了解,但在这里我试图找到"最佳解决方案"。
假设我只有一个包含C++函数的.dll。我只知道有一个带有此签名的函数:
static void GetInfos(LibraryInfo& infos);
当然,我知道有一个类 图书馆信息
class LIBRARY_EXPORT_CLASS LibraryInfo
{
public:
const char* libVersion;
const char* libName;
const char* libDate;
};
};
现在我尝试在 C# 测试项目中使用此函数:
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
// Get the 100 first byte (used only to demonstrate)
byte[] buffer = new byte[100];
Marshal.Copy(ptr, buffer, 0, 100);
// Display memory content
Console.WriteLine(Encoding.ASCII.GetString(buffer));
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
这段代码给我作为输出
v.1.2.5 ?I9
- 第一:我知道这是非常糟糕的方式,编组一个 作为 byte[] 的任意长度在这里仅用于演示。
- 第二:我只有版本,但是如果我调用相同的dll 从一个C++项目中,我有带有数据的 3 字段。
第三:我为什么要用元帅副本,这个任意长度的100 ?因为我没有成功调用 PtrToStruct,所以这是我的 试:
[StructLayout(LayoutKind.Sequential)] private struct LibInformation { public IntPtr Version; // I tried, char[], string, and IntPtr public IntPtr Name; public IntPtr Date; } static void Main(string[] args) { // Create Pointer IntPtr ptr; // Call function GetInfos(out ptr); if (ptr != IntPtr.Zero) { LibInformation infos = (LibInformation)Marshal.PtrToStructure(ptr, typeof(LibInformation)); } Console.ReadLine(); }
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.cdecl, EntryPoint = "GetInfos")] 私有静态外空 GetInfos(out IntPtr test);
然后我无法检索我的版本、名称和日期。
- 如果我在我的结构中使用 IntPtr,我没有字符串的长度,所以 我真的不能元帅。复制,也不是 PtrToStringAuto。
- 如果我使用 Char[] 或字符串,它不起作用。
我认为我的问题是不知道最终响应的大小。 所以我现在最好的选择是制作C++项目,从那里调用这个函数,然后将这个结构包装在一个更好的结构中,我可以在另一边封送(每个成员的长度作为其他成员)。
有什么想法吗?
[编辑 1 基于 jdweng 评论]
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version; // I tried, char[], string, and IntPtr
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
var data = Marshal.PtrToStructure<LibInformation>(ptr);
var version = Marshal.PtrToStringAnsi(data.Version);
Console.WriteLine(version) // result : ""
// Use Ptr directly as string instead of struct
var version2 = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(version2) // result : "v1.2.5" but how can i access other field ?
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
[编辑 2 基于 jdweng 2snd 评论]
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version;
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer for my structure
IntPtr ptr;
// Create 3 pointers and allocate them
IntPtr ptrVersion = Marshal.AllocHGlobal(100);
IntPtr ptrName = Marshal.AllocHGlobal(100);
IntPtr ptrDate = Marshal.AllocHGlobal(100);
// Then declare LibInformation and assign
LibInformation infos = new LibInformation();
// Here is probably my missunderstanding (an my error)
// As I need a ptr to call my function I have to get the Ptr of my struct
IntPtr ptr = Marshal.AllocHGlobal(300);
Marshal.StructureToPtr(infos, ptr, false);
// Assign
infos.Version = ptrVersion;
infos.Name = ptrName;
infos.Date = ptrDate;
// Call function
GetInfos(out ptr);
var data = Marshal.PtrToStructure<LibInformation>(ptr);
var version = Marshal.PtrToStringAnsi(data.Version);
Console.WriteLine(version) // result : still ""
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
我终于找到了方法,这要归功于@jdweng的解释和@PaulF的链接
唯一的坏消息是,在调用函数之前,我必须任意分配 n 个字节
链接: MSDN : 封送-类-结构-和联合
解释(jdweng):
方法的参数列表位于执行堆栈上。从方法返回后,参数列表无效,因为它可能被父代码覆盖。只有方法的返回值有效。因此,您必须在调用该方法之前分配所有数据。因此,您需要在未管理的内存中分配变量版本、名称和日期。然后声明 LibInformation 并将版本、名称和日期设置为分配的内存位置。最后调用该方法。然后,若要获取这三个变量,必须调用 Marshal.PtrToStruct 以从非托管内存复制结果。
最终代码 :
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version;
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create 3 pointers and allocate them
IntPtr ptrVersion = Marshal.AllocHGlobal(100);
IntPtr ptrName = Marshal.AllocHGlobal(100);
IntPtr ptrDate = Marshal.AllocHGlobal(100);
// Then declare LibInformation and assign
LibInformation infos = new LibInformation();
// Assign
infos.Version = ptrVersion;
infos.Name = ptrName;
infos.Date = ptrDate;
// Call function
GetInfos(out infos);
var version = Marshal.PtrToStringAnsi(data.Version);
var name = Marshal.PtrToStringAnsi(data.Name);
var date = Marshal.PtrToStringAnsi(data.Date);
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out LibInformation test); // Changing IntPtr to LibInformation
[根据大卫·赫弗南的评论编辑1]
下面是一个调用示例的 C 代码:
HComplexCTXPoint::LibraryInfo versionInfo;
HComplexCTXPoint::GetInfos(versionInfo);
std::cout << versionInfo.libName << std::endl;
- 如何为 c++ 的不同变量类型的结构元素创建动态数组?
- 如何在C++中为堆栈动态创建结构?
- C++具有动态分配的字符数组的结构
- 如何为结构字段动态分配字符空间
- 使用列表进行动态结构分配
- 第一次尝试使用new动态创建结构数组,程序挂起没有错误
- 指向动态内存中结构中的变量时出现问题
- 具有字符串成员的结构的动态数组
- 如何在 c++ 中传递结构的动态数组?
- 指向结构中的数组的指针,其中每个字段都是一个动态数组
- OpenCL 中结构数组中的动态数组
- 结构内动态分配的数组
- 如何在构造函数中初始化结构体的动态数组?
- 使用C++动态分配结构的多维数组
- 如何修复访问动态数组中结构中的字符串变量时"segmentation fault (core dumped)"错误
- C++类内部结构动态分配的方法
- 为结构动态分配内存
- MPI_Send+结构+动态内存分配
- C++ 用户输入的结构动态数组
- 结构继承层次结构-动态下转换