C ++ Struct in C #

I am using a DLL written in C ++ in my C # project using DllImport and one of the functions I am using looks like this:

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

      

The parResults structure is defined in C ++ as follows:

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 ++ function start

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

      

How can I define the above structure in C # to send this structure as an en reference to a DLL function?

This is what I've tried but doesn't work at all and I just get an access violation error.

    [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];
        }
    }

      

+3


source to share


1 answer


This is a pretty tricky structure for a marshal in C #. There are various ways to try it, but I think it would be cleanest to represent character arrays as byte and marshal arrays to and from strings manually. Here's a demo of what I mean:

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]);
    }
}

      

FROM#



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();
        }
    }
}

      


Here I have used a class to represent the structure. Since a class in C # is a reference, we do not declare parameters of this type with ref

. I also used it __cdecl

for convenience so that there was no need to figure out what the decorated function name would be. But your library used __stdcall

, so you need to stick with that.

The class demonstrates sending data in both directions. You could probably simplify the code if the data flow was more limited.

+2


source







All Articles