2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【.dll】C++からC#に値を渡したり、関数を実行したい。

Posted at

はじめに

先日、Unity6(C#)にC++から値を渡したい記事 を公開しましたが「別にUnityに限定しなくてもいいんじゃない?」と思い、書き直すことにしました。
内容は前回よりもパワーアップしております。

やりたいこと

C#からC++の関数を呼び出し、戻り値として値や文字列、配列を受け取りたいです。

前回は、C++側からの戻り値を単にコンソールに表示するだけでしたが、
今回はC#側から引数を渡し、それを基にC++側で一度処理を行ったうえで結果を返すようにします。

具体的には以下のような型の関数をC#から呼び出します。

・int
・double
・bool
・文字列 (const char*)
・int配列 (int[])
・void (戻り値なし)

環境

・Visual Studio 2022 Community
・C++ 17
・C# 7.3
・.NET Framework 4.8

.dllプロジェクトを作成する

今回はVisual Studio 2022 の Community版を使用します。

新しいプロジェクトの作成(N)Windowsデスクトップウィザード
パス&プロジェクト名設定Windowsデスクトッププロジェクト
と進み、

アプリケーションの種類(T): ダイナミックライブラリ(.dll)
追加のオプション: 空のプロジェクト(E)
image.png
と設定します。
(今回はプロジェクト名をReturnValueとしています。)

C/C++側

ReturnValue.hReturnValue.cppを作成し、以下のように記述します。

ReturnValue.h

#pragma once

#ifdef RETURNVALUE_EXPORTS
	#define EXPORT __declspec(dllexport)
#else
	#define EXPORT __declspec(dllimport)
#endif

extern "C" EXPORT int ReturnInt(int i);
extern "C" EXPORT double ReturnDouble(double d);
extern "C" EXPORT bool ReturnBool(bool b);
extern "C" EXPORT const char* ReturnString(const char* c);
extern "C" EXPORT const int* ReturnIntAry(int add, std::size_t* size);
extern "C" EXPORT void ShowFuncSig();

ReturnValue.cpp

#include <string>
#include <iostream>
#include "ReturnValue.h"

// 123 に int型 の引数を足して返す
__declspec(dllexport) int ReturnInt(int i)
{
	return 123 + i;
}

// 1.23 に double型 の引数を足して返す
__declspec(dllexport) double ReturnDouble(double d)
{
	return 1.23 + d;
}

// bool型 の引数を受け取って返す
__declspec(dllexport) bool ReturnBool(bool b)
{
	return b;
}

// Hello, C++!\n に const char*型 の引数を足して返す
__declspec(dllexport) const char* ReturnString(const char* c)
{
	static std::string result;
	result = std::string(c) + "Hello, C++!\n";

	return result.c_str();
}

// int型配列 の各要素に int型引数 を足して返す
__declspec(dllexport) const int* ReturnIntAry(int add, std::size_t* size)
{
	static const int array[] = { 1, 3, 5, 7, 9, 11, 13 };

	constexpr std::size_t N = sizeof(array) / sizeof(array[0]);
	static int result[N];

	for (std::size_t i = 0; i < N; ++i)
		result[i] = array[i] + add;

	if (size) *size = N;

	return result;
}

// 現在のソース行番号 と 関数シグネチャ を表示する 
__declspec(dllexport) void ShowFuncSig()
{
	std::cout << __LINE__ << ": " << __FUNCSIG__ << '\n';
}

C#呼び出し側

using System;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    internal class Program
    {
        [DllImport("ReturnValue.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern int ReturnInt(int i);

        [DllImport("ReturnValue.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern double ReturnDouble(double d);

        [DllImport("ReturnValue.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern bool ReturnBool(bool b);

        [DllImport("ReturnValue.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr ReturnString(string s);

        [DllImport("ReturnValue.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr ReturnIntAry(int add, out UIntPtr size);

        [DllImport("ReturnValue.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern void ShowFuncSig();

        static void Main(string[] args)
        {
            // 123 に int型 の引数を足して返す
            Console.Write($"int: {ReturnInt(123333)}\n");


            // 1.23 に double型 の引数を足して返す
            Console.Write($"double: {ReturnDouble(0.00456)}\n");


            // bool型 の引数を受け取って返す
            Console.Write($"bool: {ReturnBool(true)}\n");


            // Hello, C++!\n に const char*型 の引数を足して返す
            IntPtr pStr = ReturnString("From C#: ");
            string str = Marshal.PtrToStringAnsi(pStr);
            Console.Write(str);


            // int型配列 の各要素に int型引数 を足して返す
            IntPtr p = ReturnIntAry(1, out var sizeUP);
            if (p == IntPtr.Zero) return;

            int len = checked((int)sizeUP);
            int[] result = new int[len];
            Marshal.Copy(p, result, 0, len);

            Console.Write($"int[]: {string.Join(", ", result)}");


            // .dll側のソース行番号 と 関数シグネチャ を表示する 
            Console.WriteLine("");
            ShowFuncSig();
        }
    }
}

結果出力

int: 123456
double: 1.23456
bool: True
From C#: Hello, C++!
int[]: 2, 4, 6, 8, 10, 12, 14
45: void __cdecl ShowFuncSig(void)

なにか間違っている点があれば教えてください。

参考文献
C++ライブラリ(DLL)をUnity(C#)向けに作成して利用するシンプルな方法
C#からC/C++の関数をコールする方法 まとめ①

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?