简体   繁体   English

将C ++函数包装为具有结构的总动态大小的c#

[英]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               
  • First : I know this is the really bad way of doing it, marshalling an arbitrary length as a byte[] is only here just for demonstration. 首先:我知道这样做是非常糟糕的方法,将任意长度编组为byte []仅用于演示。
  • Second : I only have the version, but if I'm calling the same dll from a C++ project, I have the 3 field with data. 其次:我只有版本,但是如果我从C ++项目中调用相同的dll,则将包含数据的3字段。
  • 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. 然后,我将无法检索我的版本,名称和日期。

  • If I use IntPtr in my struct, I dont have the length of the String so I can't realy marshal.Copy, nor PtrToStringAuto. 如果在结构中使用IntPtr,则没有String的长度,因此无法真正使用marshal.Copy或PtrToStringAuto。
  • If I use Char[] or string it doesn't work. 如果我使用Char []或字符串不起作用。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM