[英]Wrapping c++ function to c# with total dynamic size of struct
I'm struggling with wrapping C++ function to C#. 我正在努力将C ++函数包装到C#中。 I have very basic knowledge about these sort of wrapping but here i'm trying to find the "best solution". 我对这种包装非常了解,但是在这里我试图找到“最佳解决方案”。
Let's say I only have a .dll that contains C++ function. 假设我只有一个包含C ++函数的.dll。 I only know there is a function with this signature : 我只知道有一个带有此签名的函数:
static void GetInfos(LibraryInfo& infos);
And of course I know there is a class LibraryInfos 当然,我知道有一个类LibraryInfos
class LIBRARY_EXPORT_CLASS LibraryInfo
{
public:
const char* libVersion;
const char* libName;
const char* libDate;
};
};
Now I try to use this function in a C# test project : 现在,我尝试在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);
This code give me as output 这段代码给我作为输出
v.1.2.5 ?I9
Third : Why did I use Marshal copy, and this arbitrary length of 100 ? 第三:为什么我要使用元帅副本,而这个任意长度为100? Because I didn't succeed in calling PtrToStruct, here is what I tried : 因为我未成功调用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")] private static extern void GetInfos(out IntPtr test); [DllImportAttribute(“ MyLibrary.dll”,CallingConvention = CallingConvention.Cdecl,EntryPoint =“ GetInfos”)]私有静态外部无效GetInfos(在IntPtr测试中);
Then I'm not able to retrieve my Version, Name and Date. 然后,我将无法检索我的版本,名称和日期。
I think my issue is about not knowing the size of the final response. 我认为我的问题是不知道最终答复的大小。 so my best option for now is to make C++ project, calling this function from there then wrap this struct in a better one , that I can Marshal on the other side (with Length of each member as other member). 因此,我目前最好的选择是制作C ++项目,从那里调用此函数,然后将此结构包装到更好的结构中,这样我就可以在另一端进行封送处理(每个成员的长度都作为另一个成员)。
Any thought ? 任何想法 ?
[EDITS 1 Based on jdweng comment] [编辑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);
[EDITS 2 Based on jdweng 2snd comment] [编辑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);
I finally found the way to do it, thanks to the explanation of @jdweng and the link of @PaulF 由于@jdweng的解释和@PaulF的链接,我终于找到了解决方法
The only bad news is that I have to arbitrary allocate n bytes before calling my functions 唯一的坏消息是我必须在调用函数之前任意分配n个字节
Link : MSDN : marshaling-classes-structures-and-unions 链接: MSDN:封送处理类,结构和联合
Explanations (jdweng): 说明(jdweng):
The parameter list of a method is on the execution stack. 方法的参数列表在执行堆栈上。 Once you return from the method the parameters list is not valid because it can be over written by the parent code. 从方法返回后,参数列表将无效,因为它可能会被父代码覆盖。 Only the return value of a method is valid. 仅方法的返回值有效。 So you have to Allocate all the data before calling the method. 因此,您必须在调用该方法之前分配所有数据。 So you need to allocate the variables version, name, and date in unmangaged memory. 因此,您需要在未管理的内存中分配变量的版本,名称和日期。 Then declare LibInformation and set version, name, and date to the memory locations allocated. 然后声明LibInformation并将版本,名称和日期设置为分配的内存位置。 Finally call the method. 最后调用该方法。 Then to get the three variables you have to call Marshal.PtrToStruct to copy results from unmanaged memory. 然后,要获取三个变量,必须调用Marshal.PtrToStruct来从非托管内存中复制结果。
Final code : 最终代码:
[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
[Edit 1 based on David Heffernan comment] [根据David Heffernan的评论编辑1]
Here is the C code of a calling sample : 这是调用示例的C代码:
HComplexCTXPoint::LibraryInfo versionInfo;
HComplexCTXPoint::GetInfos(versionInfo);
std::cout << versionInfo.libName << std::endl;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.