渋谷ほととぎす通信

エンジニア社長によるUnityとAIのブログ & エンジニアの生存戦略

【Unity】C#からC++に文字列を渡す方法

【Unity】C#からC++に文字列を渡す方法

こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • C#とC++間でデータをやり取りしたい
  • C#からC++に文字列を転送できる?
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    C#とC++間のデータやりとりを実装したことありますでしょうか?

    Unityが提供する機能を普通に使う状況ではあまり登場しないシチュエーションかも知れません。

    Unity提供外の機能にアクセスしたくなると途端にC++や別の言語が必要になります。

    お悩みさん
    お悩みさん
    Unity提供外の機能って何?

    Unity提供外の機能とは例えばiOSやAndroidと言ったOS独自のネイティブ機能です。もちろんUnityも数多くサポートしていますが、全てを網羅しているわけではありません。

    特にOSのアップデートで登場した新機能はUnityのサポートが間に合わず開発者が実装するケースもよくあります。

    本記事では若干ニッチな内容ですが、C#とC++間のデータやり取りの基本編として解説していきます。

    今回は第一歩として C#からC++へ「文字列」の送信 です。

    これからOS独自のネイティブ機能にアクセス、C++でライブラリを開発していこうと思っている方はぜひ最後まで読んでみてください。

    C#からC++に文字列を渡すために4つのポイントを抑えよう

    最初に結論です。

    C#からC++に文字列を渡すために4つのポイントを抑えよう

    ①C++はchar型のポインタで受け取る

    ②C++のメソッドに「extern ”C”」をつける

    ③C#側は「DllImport」属性をつける

    ④C#側に「static extern」を付与

    C#からC++に文字列データを渡す際は以上の4つのポイントを抑えておけば大丈夫です。やり方が分かれば意外と簡単かもしれません。

    大まかな作業手順はこちらです。

    1. C++を使ってDLLファイルの作成
    2. DLLファイルをUnityへ取り込む
    3. DLLファイルをC#から呼び出す

    以上の3ステップで実装していきます。

    今回はサンプルとしてC#側から文字列を送信しC++側でコンソール上にログを出力してみます。

    本記事はWindows環境限定なので注意してください。

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    DLLファイル作成するための環境構築

    では早速C++をDLL化していくのですが、その前に環境構築していきましょう。

    C++からDLLファイルを作成するためのツールとして、Visual Studioを使っていきます。詳しくはこちらの記事を参考に 新しいプロジェクトを作成 してみてください。

    C#側に公開するC++の関数定義

    まずはC#からC++を呼ぶための準備をしていきます。やるべきことはC#から実行できるC++側の関数定義です。

    次のソースコードのように extern "C" __declspec(dllexport) を使ってC#から呼び出す関数を定義していきます。

    extern "C" __declspec(dllexport) 関数名();  
    

    C#から呼ばれる関数には↑上記の「extern "C" __declspec(dllexport)」の記述必要になります。

    C#から呼び出したい関数全てに記述するのは面倒なので、マクロを使って効率化することもできますが、本記事は あくまで入門記事。 マクロを使わず愚直に実装していきます。

    C++側でC#の文字列を受け取る処理

    早速C++側の実装を進めていきましょう。C#側からC++で定義した関数を実行するタイミングで文字列が送信されます。

    最初に重要なことをお伝えしますが C#からC++にString型をそのまま送れません。 C++側はC#の文字列を ポインタ で受け取る必要があるのです。

    C#から文字列を受け取るC++のコードはこちら。

    #include <iostream>
    
    // C#から呼ばれる関数の定義  
    extern "C" __declspec(dllexport) void ReceiveString(const char* str)  
    {
        if (str == NULL) {  
            std::cout << "str is null." << std::endl;  
            return;  
        }
        std::cout << str << std::endl;  
    }
    

    C#から実行される関数「ReceiveString」を定義しました。

    C++はC#と違って文字列をString型で扱うのではなく char型のポインタ で受け取ります。

    このデータ変換(ここで言うString型からchar型のポインタへの変換)のことを 「マーシャリング」 と言います。マーシャリングについてはこちら記事で詳しく解説しているのでぜひ合わせて読んでみてください。

    ReceiveString関数で実装した内容はC#から送られたの文字列を cout でコンソールに出力するというシンプルな処理になっています。

    Unityからの実行を分かりやすくするための工夫

    C#からC++に文字列を送信する事自体は先のC++で実現可能です。しかし、UnityからC++を実行してもコンソールが表示されず、本当に文字列が送信できたのか確認できません。

    そこでC++の処理を少し改変します。

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <windows.h>
    
    // C#から呼ばれる関数の定義  
    extern "C" __declspec(dllexport) void ReceiveString(const char* str)  
    {
        // コンソールを開く  
        FreeConsole();  
        AllocConsole();  
    
        // coutの出力をコンソールに表示させる  
        freopen("CONOUT$", "w", stdout);  
        freopen("CONOUT$", "w", stderr);  
        if (str == NULL) {  
            std::cout << "str is null." << std::endl;  
            return;  
        }
        std::cout << str << std::endl;  
    }
    

    以上のようにC++コードを修正します。

    修正内容によりUnityから実行した際に新規でコンソールが起動します。そしてC#から送られた文字列がコンソールに表示されるようになります。

    C++のDLL化

    C++の処理が完了したら、次にやるべきことはC#からC++の処理を呼び出し処理です。C#からC++を呼び出す方法はいくつかありますが、今回はDLLファイルに固めてC#からアクセスする方法を取ります。

    【Unity】C#からC++に文字列を渡す方法_0

    Visual Studioの メニュービルド から 「ソリューションのビルド」 を実行します。

    すると TestDll.dll が作成されます。このDLLファイルをUnityプロジェクト内の Pluginsフォルダ内に格納 しましょう。

    Pluginsフォルダがない場合は新規作成してください。

    C#側でDLLをインポートしてC++のメソッドを呼ぶ

    次にC#側の実装に移ります。Unityエディタで新規のC#スクリプトを作成しHierarchy上のGameObjectにAddComponentしておいてください。

    C#側のソースコード(抜粋)はこちらです。

    [DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]  
    static extern void ReceiveString(string str);  
    
    void Awake()  
    {
        ReceiveString("Hello World C# to C++!!");  
    }
    

    C++製DLLファイルを読み込み、DLL内の関数を実行しています。

    上記C#ソースコードの中でポイントは3点。

    1. C++で定義した関数と 同名の関数 を定義
    2. 関数に static extern を付与
    3. [DllImport("DLLファイル名", CallingConvention = CallingConvention.Cdecl)] 属性を付与

    この状態でUnityを実行してみましょう。すると新規でコンソールが起動し、 Hello World C# to C++!! と出力されます。

    【Unity】C#からC++に文字列を渡す方法_1

    以上でC#からC++に文字列を送信できました。お疲れ様です。

    上記C#コードの全文は本記事の最後で公開しています。

    まとめ : C#からC++(DLL)に文字列を渡す

    本記事ではC#からC++に文字列を送信する手順を紹介してきました。

    • C++は char型のポインタ で受け取る
    • C#側にC++のメソッドを公開するためにextern "C"が必要
    • C#側はDllImport属性が必要
    • C#側にC++で定義した関数にはstatic externを付与

    以上のポイントを抑えておきましょう。

    C#/C++間データやり取りの基礎となる情報です。

    C++はchar型のポインタで受け取るという点以外は、C#側からはString型をそのまま送れるため、実装自体はシンプルだったのではないかと思います。

    引き続きさまざまなデータの型をC#/C++間でやり取りしていきます。

    今回使用したソースコード全文はこちらです。

    参考にしてみてください。

    オススメ記事
    検証環境
    • Visual Studio Community 2022 (64 ビット) v17.13.1
    • Windows11
    • Unity6000.0.39f1
    参考サイト