【C++/C#】C++で作成したDLLをC#で呼ぶ

**もくじ**
→https://tera1707.com/entry/2022/02/06/144447

# やりたいこと
C++で作ったDLLを、C#で使いたい。

# 参考
呼び出し規約の種類(cdeclとか、stdcallとか)
https://konuma.org/blog/2006/01/02/post_1fd3/

extern "C" とは？
https://konuma.org/blog/2006/01/04/post_144e/

DLLを静的リンクで呼び出す
http://yamatyuu.net/computer/program/sdk/base/static_dll/index.html

# C++側（呼ばれる側=DLL）
### ヘッダーファイル
#### __declspec(dllexport) と __declspec(dllimport)
マクロを使用して、エクスポートする側(関数の実装を書いているcppにインクルードするとき)には「declspec(dllexport)」を書く。
インポートする側(dllを使う側のcppでインクルードするとき)には、「declspec(dllimport)」を書く。

→詳細は、下記を参照
https://konuma.org/blog/2006/01/02/post_1fd3/

#### extern "C"
通常、DLLを作成するときは、C形式のDLLとして作成する。
C形式：マングリングが行われない
C++形式：マングリングが行われる

→詳細は、下記を参照
https://konuma.org/blog/2006/01/04/post_144e/


``` DllTest.h
#include <Windows.h>
#include <string.h>

// エクスポートとインポートの切り替え
#ifdef VC_DLL_EXPORTS
#undef VC_DLL_EXPORTS
#define VC_DLL_EXPORTS extern "C" __declspec(dllexport)
#else
#define VC_DLL_EXPORTS extern "C" __declspec(dllimport)
#endif

// エクスポート関数のプロトタイプ宣言
// 通常、__stdcallを適用する(__stdcall = WINAPI)。
VC_DLL_EXPORTS void __cdecl Test_MyApi();
VC_DLL_EXPORTS void __cdecl Test_MyApi2(const wchar_t* lpText, const wchar_t* lpCaption);
VC_DLL_EXPORTS void __cdecl Test_MyApi3(int count);
```

### cppファイル

``` DllTest.h
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

#define VC_DLL_EXPORTS
#include "DllTest.h"

// エクスポート関数の実装
void __cdecl Test_MyApi()
{
	const wchar_t* lpText = L"Test\0";
	wprintf_s(lpText);
}

void __cdecl Test_MyApi2(const wchar_t* lpText, const wchar_t* lpCaption)
{
	// MessageBoxを呼び出すだけ。
	wprintf_s(L"%s", lpText);
	wprintf_s(L"%s", L" ");
	wprintf_s(L"%s", lpCaption);
}

void __cdecl Test_MyApi3(int count)
{
	for (int i = 0; i < count; i++)
	{
		wprintf_s(L"%d,", i);
	}
}
```

# C#側（呼ぶ側）

``` program.cs
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            NativeMethods.Test_MyApi(1);

            NativeMethods.Test_MyApi2("テキスト", "キャプション");

            NativeMethods.Test_MyApi3(3);
        }
    }

    public static class NativeMethods
    {
        [DllImport("DllTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
        public extern static void Test_MyApi(int param);

        [DllImport("DllTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
        public extern static void Test_MyApi2(string lpText, string lpCaption);

        // 下記でも、うまく動く。(CharSetの代わりにMarshallAsでLPWSTRを指定する)
        //[DllImport("DllTest.dll", CallingConvention = CallingConvention.Cdecl)]
        //public extern static void Test_MyApi2([MarshalAs(UnmanagedType.LPWStr)]string lpText, [MarshalAs(UnmanagedType.LPWStr)]string lpCaption);

        [DllImport("DllTest.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
        public extern static void Test_MyApi3(int count);
    }
}

```
# 不明点
- C#側で、C++の関数に文字列を渡すとき、stringで渡してwchar_tで受けてるが、本当にこれでOK？(C#のstringは、C++の何の型にあたる？？)

→albireo様にコメント頂きました。
C++の文字列変数は「メモリ上のアドレス」を指している。
C#の文字列はマネージドメモリ上を参照しているため、特定のメモリアドレスを持っていない。
C++側に文字列型を渡すときは、「マーシャリング」という変換処理を自動で行う。

「[MarshalAs(UnmanagedType.LPWStr)]」などで、マーシャリングの方法をコンパイラに指定する。


# コード
https://github.com/tera1707/WPF-/tree/master/020_CppDllProject