C#からC/C++の関数をコールして構造体を渡す


はじめに

当記事は @TackKaiware さまの、
「C#からC/C++の関数をコールする方法 まとめ①」 を基に作成されています。
C#からC++の関数コールをして、構造体を構造体ポインタとして渡してみます。

ソースコード

C++ dll ソースコード

mylib.h
#include <Windows.h>

typedef struct _MY_STRUCT {
    bool myFlag;
    char* name;
    void* output;
};

extern "C" __declspec(dllexport) int Test(PVOID mystruct);
mylib.cpp
#include <iostream>
#include "mylib.h"

int Test(PVOID mystruct)
{
    _MY_STRUCT* ms = (_MY_STRUCT*)mystruct;

    std::cout << "CPPの関数が呼び出されました。" << std::endl;

    if (ms->myFlag)
    {
        std::cout << "Flag on" << std::endl;
    }
    else {
        std::cout << "Flag off" << std::endl;
    }

    std::cout << "name: " << ms->name << std::endl;

    std::cout << "output: " << ms->output << std::endl;

    return 634;
}

C# WinForm

MyForm.cs
public struct _MY_STRUCT
{
    public bool myFlag;
    public string name;
    public float output;
}

[DllImport("mylib.dll")]
public unsafe static extern int Test(IntPtr pStructure);

private unsafe void Button1_Click(object sender, EventArgs e)
{
    _MY_STRUCT myStruct = new _MY_STRUCT();
    myStruct.myFlag = true;
    myStruct.name = "SETOKOUJI";
    myStruct.output = 999.9f;

    IntPtr pStructure = Marshal.AllocCoTaskMem(Marshal.SizeOf(myStruct));
    Marshal.StructureToPtr(myStruct, pStructure, false);

    int x = Test(pStructure);
    WriteLog(x.ToString());

    Marshal.FreeCoTaskMem(pStructure);
}

実行結果

WinForm:(関数の返り値)

構造体のマーシャリング

構造体を「構造体ポインタ」として渡す為、該当構造体をマーシャリングする必要があります。
アンマネージドメモリを確保し、Marshal.StructureToPtrを使用して構造体ポインタへと変換します。

IntPtr pStructure = Marshal.AllocCoTaskMem(Marshal.SizeOf(myStruct));
Marshal.StructureToPtr(myStruct, pStructure, false);

ポインタへ変換されていることが確認できます。

今回は各チェックは省いています。

if(pStructure == IntPtr.Zero || pStructure == null)
{
    return;
}

最後にリソースを開放します。

Marshal.FreeCoTaskMem(pStructure);

C++側: Voidポインタから構造体への変換

PVOID mystructはVoidポインタ(void*)です。
関数コールの際に構造体ポインタを受け取ります。

int Test(PVOID mystruct)
{
    _MY_STRUCT* ms = (_MY_STRUCT*)mystruct;
}

構造体ポインタから構造体へのキャストは、下記でも同様です。

_MY_STRUCT* ms = static_cast<_MY_STRUCT*>(mystruct);

おわり

今回は、C#からC/C++の関数をコールして構造体を渡してみました。
同様にして、dll側から返り値として構造体を渡すこともできます。