🔬

UnityのWindows C++ソースコードプラグインは__declspec(dllexport)しなくてもよい

2021/04/22に公開

はじめに

UnityではScripting Backendをil2cppにするとC++のソースを直接Pluginsフォルダに入れて利用できるようになりました。

このうちWindowsのC++ソースコードプラグインのドキュメントは以下の説明とサンプルコードがあります。

関数は生成された C++ コードでまとめてリンクされているため、_P/Invoke への別の DLL はありません

extern "C" __declspec(dllexport) int __stdcall CountLettersInString(wchar_t* str)
{
    int length = 0;
    while (*str++ != L'\0')
        length++;
    return length;
}

ですが、まとめてリンクされるなら外部から呼び出されないので__declspec(dllexport)は宣言しなくてもよいはずです。
そこで本当に宣言しなくてもよいのか検証してみました。

__declspec(dllexport)ありでビルド

ドキュメントと同じ以下のC++ソースコードプラグイン[1:1]と一緒にアプリをビルドして実行すると引数の文字列の長さを返してくれます。

NewBehaviourScript.cs
[DllImport("__Internal")]
private static extern int
CountLettersInString([MarshalAs(UnmanagedType.LPWStr)]string str);
NativeFunctions.cpp
extern "C" __declspec(dllexport) int __stdcall CountLettersInString(wchar_t* str)
{
    int length = 0;
    while (*str++ != L'\0')
        length++;
    return length;
}

それではビルドしたGameAssembly.dllの定義はどうでしょうか?

>dumpbin /exports GameAssembly.dll | findstr CountLettersInString
          2    1 00109360 CountLettersInString = CountLettersInString

dumpbinで確認すると確かにC++ソースコードプラグインに定義した関数がエクスポートされています。

__declspec(dllexport)なしでビルド

今度は以下のように__declspec(dllexport)だけを削除してビルドしたアプリを実行しても同様に引数の文字列の長さを返してくれます。

NativeFunctions.cpp
extern "C" int __stdcall CountLettersInString(wchar_t* str)
{
    int length = 0;
    while (*str++ != L'\0')
        length++;
    return length;
}

先程と同様にGameAssembly.dllの定義を確認すると今度は存在しません。

>dumpbin /exports GameAssembly.dll | findstr CountLettersInString

それならビルドした時に<ProductName>_ButDontShipItWithYourGameに出力されたソースコードを調べるとAssembly-CSharp.cppから以下のようにCountLettersInString関数を呼び出しています。
このフォルダにはC++ソースコードプラグインのNativeFunctions.cppも一緒に出力されているので__declspec(dllexport)を宣言しなくてもCountLettersInString関数を呼び出せるのです。

<ProductName>_ButDontShipItWithYourGame\il2cppOutput\Assembly-CSharp.cpp
// System.Int32 NewBehaviourScript::CountLettersInString(System.String)
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t NewBehaviourScript_CountLettersInString_mED52AF1B6313A9C16FA9E483C3444661B539A17E (String_t* ___str0, const RuntimeMethod* method)
{
	typedef int32_t (DEFAULT_CALL *PInvokeFunc) (Il2CppChar*);

	// Marshaling of parameter '___str0' to native representation
	Il2CppChar* ____str0_marshaled = NULL;
	if (___str0 != NULL)
	{
		____str0_marshaled = ___str0->get_address_of_m_firstChar_1();
	}

	// Native function invocation
	int32_t returnValue = reinterpret_cast<PInvokeFunc>(CountLettersInString)(____str0_marshaled);

	return returnValue;
}

まとめ

Windowsでil2cppのC++ソースコードプラグインは__declspec(dllexport)を宣言しなくても問題なく呼び出せる事が分かりました。
そのためクラック対策などで解析の手掛りを減らす手段の1つとなります。

脚注
  1. https://docs.unity3d.com/2018.1/Documentation/Manual/WindowsPlayerCPlusPlusSourceCodePluginsForIL2CPP.html ↩︎ ↩︎

  2. https://docs.unity3d.com/2018.1/Documentation/Manual/macOSPlayerCPlusPlusSourceCodePluginsForIL2CPP.html ↩︎

  3. https://docs.unity3d.com/2018.2/Documentation/Manual/AndroidNativePlugins.html ↩︎

  4. https://docs.unity3d.com/520/Documentation/Manual/PluginsForIOS.html ↩︎

Discussion