渋谷ほととぎす通信

「Unityをわかりやすく」初心者のためのゲーム作りブログ

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

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

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

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

Unityが提供する機能を普通に使っているだけだと、
あまり出てこないシチュエーションかも知れません。

Unityが提供していない機能にアクセス といった
Unityの外へ一歩出ようとすると
C++や別の言語が必要になります。

今回はC#とC++間のデータのやり取りの第一歩として、
C#からC++へ文字列の送信を解説します。

C#からC++に文字列を渡す4つのポイント

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

  • ① : C++は char型のポインタ で受け取る
  • ② : C++のメソッドにextern "C"をつける
  • ③ : C#側はDllImport属性をつける
  • ④ : C#側にstatic externを付与

やり方が分かれば意外と簡単です。

ざっくりした手順はコチラ

  1. C++からのTestDll.dllを生成
  2. TestDll.dllをC#から呼び出す

C#側から文字列を引き渡して、
C++側でログを出力してみます。

環境構築を含めて詳細を解説していきます。

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

【環境構築】 DLLプロジェクトを作成

Visual Studio Community 2017で開発を進めていきます。

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

新しいプロジェクトを作成し、
ダイナミックリンクライブラリ(DLL) を選びます。

C#からC++に文字列を渡す方法_2

プロジェクトを作ると
デフォルトでファイルが
いくつか作られますが気にせず進めます。

C#側に公開するC++の関数アトリビュートの定義

C#からC++に文字列を渡す方法_3

まずはC#からC++を呼ぶための準備です。

extern "C"{  
// C#から呼ばれたい関数を定義  
}

C#から呼ばれる関数に↑の記述が必要になります。
今回はマクロを使って開発効率を上げます。

#define DllExport extern "C" __declspec(dllexport)

↑の1行をstdafx.hに追加してマクロを作成。

// hoge()をC#から呼べるようになる  
DllExport void hoge();  

↑このようにDllExport属性を
関数定義の先頭に記述することで
C#から呼べるように します。

さきほど登場したstdafx.hは、
プロジェクト作成時に作られるヘッダファイルです。

このファイルの行末あたりに1行追加しておきます。

💻ソースコード : stdafx.h
#pragma once

#include "targetver.h"
// Windows ヘッダーからほとんど使用されていない部分を除外する  
#define WIN32_LEAN_AND_MEAN
// Windows ヘッダー ファイル  
#include <windows.h>
// プログラムに必要な追加ヘッダーをここで参照してください  
#define DllExport extern "C" __declspec(dllexport)

なぜextern 'C'をつけるのか?

C#からC++に文字列を渡す方法_4

そもそもなぜC#からC++を呼ぶ際に、
extern "C"が必要のか?疑問ですよね。

理由はC++のコンパイルにあります。

C++はコンパイルのたびにメソッド名が変わります。
extern "C"を記述することで、 メソッド名を固定 します、

つまりC#側から呼び出したときのリンクエラーを防ぐために必要なのです。

準備が完了したため、
C#からC++に文字列を送る実装を進めます。

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

C#からC++に文字列を渡す方法_5

最初に悲報ですが、
C#からC++にstring型をそのまま送れません。

C++側はC#の文字列を
ポインタで受け取ります。

#include "stdafx.h"
#include <iostream>

// C#から呼ばれる関数の定義  
DllExport void Test(const char* str);  

void Test(const char* str)  
{
    if (str == NULL) {  
        std::cout << "str is null." << std::endl;  
        return;  
    }
    std::cout << str << std::endl;  
}

C++側はString型ではなく、
char型のポインタで定義します。

DllExport属性をTestメソッドにくっつけることで、
C#側から実行できるようになります。

C++のDLL化

C++の処理をC#から呼び出すために
dllファイルに固めます。

C#からC++に文字列を渡す方法_6

C++側の実装が完了したのでビルドします。

  • プロジェクトを右クリック
  • ビルド

すると TestDll.dll が出来上がります。
このDLLをC#プロジェクトのexeと同階層に配置します。

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

C#からC++に文字列を渡す方法_7

C#の実装に移ります。
まずはソースから見ていきましょう。

[DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]  
static extern void Test(string str);  

static void Main()  
{
    Test("hogehoge");  
}

ポイントは3点

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

詳しいことはいったん置いておきます。

この状態でC#を実行してみます。
するとコンソールにhogehogeと出力されるでしょう。

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

C#からC++に文字列を渡す方法_8

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

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

以上のポイントを抑えておきましょう。
C#/C++間データやり取りの基礎となる情報です。

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

あわせて読みたい記事

C#からC++にintを渡す方法

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

お楽しみに!

オススメ記事
検証環境
  • VisualStudioCommunity2017 v15.9.8
  • Windows10
参考サイト