C#中的C++结构

本文关键字:结构 C++ 中的 | 更新日期: 2023-09-27 18:27:24

我在C#项目中使用DllImport使用C++编写的DLL,我使用的一个函数如下:

    [DllImport("dds.dll", CharSet = CharSet.Auto)]
    private static extern int Par(
        ddTableResults2 tableResult,
        ref parResults ParResult,
        int vul
    );

parResults结构在C++中定义如下:

struct parResults {
  /* index = 0 is NS view and index = 1 
     is EW view. By 'view' is here meant 
     which side that starts the bidding. */
  char          parScore[2][16];
  char          parContractsString[2][128]; 
};

C++函数的启动

int STDCALL Par(struct ddTableResults * tablep, struct parResults *presp, 
    int vulnerable)

我应该如何在C#中定义上述结构才能将该结构作为引用发送到DLL函数中?

这是我尝试过的,但根本不起作用,我只是得到了一个访问违规错误

    [StructLayout(LayoutKind.Sequential)]
    public struct parResults
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public char[,] parScore;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public char[,] parContractsString;
        public parResults(int x)
        {
            parScore = new char[2,16];
            parContractsString = new char[2,128];
        }
    }

C#中的C++结构

这是一个很难在C#中封送的结构。有多种方法可以尝试,但我认为将字符数组表示为字节数组并手动封送到字符串和从字符串封送字符串将是最干净的。以下是我的意思:

C++

#include <cstring>
struct parResults {
    char          parScore[2][16];
    char          parContractsString[2][128];
};
extern "C"
{
    __declspec(dllexport) void foo(struct parResults *res)
    {
        strcpy(res->parScore[0], res->parContractsString[0]);
        strcpy(res->parScore[1], res->parContractsString[1]);
    }
}

C#

using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        class parResults 
        {
            private const int parScoreCount = 2;
            private const int parScoreLen = 16;
            private const int parContractsStringCount = 2;
            private const int parContractsStringLen = 128;
            [MarshalAs(UnmanagedType.ByValArray, 
                SizeConst = parScoreCount * parScoreLen)]
            private byte[] parScoreBuff;
            [MarshalAs(UnmanagedType.ByValArray, 
                SizeConst = parContractsStringCount * parContractsStringLen)]
            private byte[] parContractsStringBuff;
            public string getParScore(int index)
            {
                string str = Encoding.Default.GetString(parScoreBuff, 
                    index * parScoreLen, parScoreLen);
                int len = str.IndexOf(''0');
                if (len != -1)
                    str = str.Substring(0, len);
                return str;
            }
            public void setParScore(int index, string value)
            {
                byte[] bytes = Encoding.Default.GetBytes(value);
                int len = Math.Min(bytes.Length, parScoreLen);
                Array.Copy(bytes, 0, parScoreBuff, index * parScoreLen, len);
                Array.Clear(parScoreBuff, index * parScoreLen + len,
                    parScoreLen - len);
            }
            public string parContractsString(int index)
            {
                string str = Encoding.Default.GetString(parContractsStringBuff,
                    index * parContractsStringLen, parContractsStringLen);
                int len = str.IndexOf(''0');
                if (len != -1)
                    str = str.Substring(0, len);
                return str;
            }
            public void setParContractsString(int index, string value)
            {
                byte[] bytes = Encoding.Default.GetBytes(value);
                int len = Math.Min(bytes.Length, parContractsStringLen);
                Array.Copy(bytes, 0, parContractsStringBuff,
                    index * parContractsStringLen, len);
                Array.Clear(parContractsStringBuff, 
                    index * parContractsStringLen + len,
                    parContractsStringLen - len);
            }
            public parResults()
            {
                parScoreBuff = new byte[parScoreCount * parScoreLen];
                parContractsStringBuff = 
                    new byte[parContractsStringCount * parContractsStringLen];
            }
        };
        [DllImport(@"...", CallingConvention = CallingConvention.Cdecl)]
        static extern void foo([In,Out] parResults res);
        static void Main(string[] args)
        {
            parResults res = new parResults();
            res.setParContractsString(0, "foo");
            res.setParContractsString(1, "bar");
            foo(res);
            Console.WriteLine(res.getParScore(0));
            Console.WriteLine(res.getParScore(1));
            Console.ReadLine();
        }
    }
}

在这里,我使用了一个类来表示结构。由于C#中的类是一个引用,所以我们不使用ref声明该类型的参数。为了方便起见,我也使用了__cdecl,以避免计算函数的修饰名称。但您的库使用了__stdcall,所以您需要坚持这一点。

该类演示双向发送数据。如果数据流受到更多限制,您可能会简化代码。