はじめに
以下の場合も、実現可能です。
- 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]); } } } }
実行結果