【问题标题】:C++ Struct in C#C# 中的 C++ 结构
【发布时间】:2014-12-18 01:34:18
【问题描述】:

我正在使用 DllImport 在我的 C# 项目中使用用 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# 中定义上述结构,以便能够将该结构作为 en 引用发送到 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++ 结构的真实定义以及您要导入的函数。您发布的结构的伪造版本让我们想知道与编译的代码和您发布的代码相比还有什么变化。
  • 我看看能不能找到!
  • struct ddTableResults 怎么样?这是如何在 C++ 和 C# 中声明的。为什么在 C# 中没有通过 ref 传递?
  • 我正在使用一些开源软件并且文档有限; privat.bahnhof.se/wb758135/dds270-src.zip 但如果使用一维数组和 ddTableResult 的引用,我设法让它工作
  • 数据流向何方?从 C# 到本机库。还是在另一个方向?或者在两个方向。我不是特别想在一些开源的非现场链接中四处寻找。我认为这是你要做的,然后在问题中提供声明。感谢您到目前为止所做的修改。

标签: c# c++ struct pinvoke dllimport


【解决方案1】:

这是一个在 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,所以你需要坚持下去。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多