nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

C#からC++のDLLを呼び出す (構造体編) [C#]

はじめに

以下の場合も、実現可能です。

  • C++のDLLのAPIに対して、構造体のデータを渡す場合
  • C++のDLLのAPIから、構造体のデータを受け取る場合

環境

  • IDE : Visual Studio Community 2017 (Version 15.7.1)

コード

#include <string>

#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif

const static int INIT_HP = 300;
const static int INIT_MP = 200;
const static int MAX_NAME_LENGTH = 256;
const static int BIG_DATA_LENGTH = 100000;

struct MainCharacter_t
{
    char name[MAX_NAME_LENGTH];
    int hp;
    int mp;
    unsigned char bigData[BIG_DATA_LENGTH];
};

MainCharacter_t MainCharacter = { "Ichiro", INIT_HP, INIT_MP, 0 };


DLLEXPORT void SetCharaData(MainCharacter_t *someData_t)
{
    memcpy_s(&MainCharacter, sizeof(MainCharacter_t), someData_t, sizeof(MainCharacter_t));
}

DLLEXPORT void GetCharaData(MainCharacter_t *someData_t)
{
    memcpy_s(someData_t, sizeof(MainCharacter_t), &MainCharacter, sizeof(MainCharacter_t));
}

// テストデータセット用[f:id:nprogram:20180522230541p:plain]
DLLEXPORT void SetBigData()
{
    unsigned char tempBigData[BIG_DATA_LENGTH] = { 0 };

    for (int i = 0; i < BIG_DATA_LENGTH; i++)
    {
        tempBigData[i] = i % 256;
    }

    memcpy_s(MainCharacter.bigData, BIG_DATA_LENGTH, tempBigData, BIG_DATA_LENGTH);
}

[C#のコード]

using System;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        public const int MAX_NAME_LENGTH = 256;
        public const int BIG_DATA_LENGTH = 100000;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct MainCharacter_t
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_NAME_LENGTH)]
            public string imageFileName;
            public int hp;
            public int mp;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = BIG_DATA_LENGTH)]
            public byte[] bigData;
        }

        // テストデータセット用
        [DllImport("CPlusDLL", EntryPoint = "SetBigData", CallingConvention = CallingConvention.Cdecl)]
        static extern void _SetBigData();

        [DllImport("CPlusDLL", EntryPoint = "SetCharaData", CallingConvention = CallingConvention.Cdecl)]
        static extern void _SetCharaData(IntPtr someCharaData);

        [DllImport("CPlusDLL", EntryPoint = "GetCharaData", CallingConvention = CallingConvention.Cdecl)]
        static extern void _GetCharaData(IntPtr someCharaData);

        static void Main(string[] args)
        {
            // テストデータセット
            _SetBigData();

            ShowCharaData(GetCurrentCharaData());

            MainCharacter_t setData1 = new MainCharacter_t();
            setData1.hp = 10;
            setData1.mp = 0;
            setData1.imageFileName = "Jiro";
            SetCurrentCharaData(setData1);

            // テストデータセット
            _SetBigData();
            ShowCharaData(GetCurrentCharaData());
        }

        static public void SetCurrentCharaData(MainCharacter_t someCharaData)
        {
            // COM タスク メモリ アロケーターから、C#の構造体のサイズ分、メモリ ブロックを割り当てる
            IntPtr someCharaDataPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(someCharaData));

            // マネージ オブジェクトからアンマネージ メモリ ブロックにデータをマーシャリングする
            Marshal.StructureToPtr(someCharaData, someCharaDataPtr, false);

            _SetCharaData(someCharaDataPtr);

            Marshal.FreeCoTaskMem(someCharaDataPtr);
        }

        static public MainCharacter_t GetCurrentCharaData()
        {
            MainCharacter_t mainCharacter = new MainCharacter_t();


            // COM タスク メモリ アロケーターから、C#の構造体のサイズ分、メモリ ブロックを割り当てる
            IntPtr mainCharacterPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(mainCharacter));

            _GetCharaData(mainCharacterPtr);

            // IntPtr変数が示すメモリに格納された情報を、Marshal.PtrToStructure()で、C#の構造体にコピーする
            mainCharacter = (MainCharacter_t)Marshal.PtrToStructure(mainCharacterPtr, mainCharacter.GetType());

            Marshal.FreeCoTaskMem(mainCharacterPtr);

            return mainCharacter;
        }

        static public void ShowCharaData(MainCharacter_t someCharaData)
        {
            Console.WriteLine("MainChara Name : " + someCharaData.imageFileName);
            Console.WriteLine("MainChara HP : " + someCharaData.hp);
            Console.WriteLine("MainChara MP : " + someCharaData.mp);

            Console.WriteLine("[Big Data First Data (5 count)]");
            for ( int i = 0; i < 5; i++)
            {
                Console.WriteLine("[" + i + "] : " + someCharaData.bigData[i]);
            }

            Console.WriteLine("[Big Data Last Data (5 count)]");
            for (int i = BIG_DATA_LENGTH - 5; i < BIG_DATA_LENGTH; i++)
            {
                Console.WriteLine("[" + i + "] : " + someCharaData.bigData[i]);
            }
        }
    }
}

実行結果

f:id:nprogram:20180522230541p:plain

参考リンク

d.hatena.ne.jp