グローバルな C スタイルのメソッドを使用する必要があります。この理由については、こちらで説明しています。
基本的には、C 関数は DLL エクスポートに適切に変換されます。これは、言語機能の点で C の方が「地面に近い」からです。C は、より直接的にマシン コードに変換されます。C++ はコンパイラ レベルで多くのことを行い、C++ 環境以外では使用できない多くの機能を提供します。このため、エクスポートされた関数は、DLL の境界を越えて適切に機能するために C スタイルに従う必要があります。つまり、テンプレートも、インライン コードも、POD 以外のクラスや構造体もありません。
次のコードを検討してください。
extern "C"
{
__declspec(dllexport) int GlobalFunc(int n)
{
return n;
}
namespace SomeNamespace
{
__declspec(dllexport) int NamespaceFunction(int n)
{
return n;
}
}
class MyClass
{
__declspec(dllexport) int ClassNonStatic(int n)
{
return n;
}
__declspec(dllexport) static int ClassStatic(int n)
{
return n;
}
};
}
これにより、次の DLL エクスポート関数名が生成されます。
?ClassNonStatic@MyClass@@AAEHH@Z
?ClassStatic@MyClass@@CAHH@Z
グローバル関数
名前空間関数
おかしな名前のプロジェクトは、Visual Studio でビルドされた C++ プロジェクト以外とは本質的に互換性がありません。これは名前マングリングと呼ばれ、私が話しているエクスポートされた関数の制限に対する回避策として、名前自体にいくつかの型情報を埋め込みます。技術的には、これらの関数を外部で使用できますが、壊れやすく、コンパイラ固有の動作のニュアンスに依存しています。
関数を DLL にエクスポートするための経験則は次のとおりです。C でこれを実行できますか? それができない場合は、問題が発生することはほぼ確実です。
ここで、静的クラス メソッド (本質的にはグローバル) でさえも、名前マングリングがあることに注意してくださいextern "C"
。ただし、名前空間内の独立した関数は、名前マングリングなしでエクスポートされます (ただし、名前空間のスコープは失われます)。
この経験則が理にかなっている理由を理解し始めることができます。
クラスをエクスポートする場合は、経験則に従って、C で行うように DLL インターフェイスを設計しましょう。以下に例を示します。この C++ クラスを見てみましょう:
class Employee
{
private:
std::string firstName;
std::string lastName;
public:
void SetFirstName(std::string& s)
{
this->firstName = s;
}
void SetLastName(std::string& s)
{
this->lastName = s;
}
std::string GetFullName()
{
return this->firstName + " " + this->lastName;
}
};
これに固執することはできません__declspec(dllexport)
。そのための C インターフェイスを提供し、それをエクスポートする必要があります。このような:
extern "C"
{
__declspec(dllexport) Employee* employee_Construct()
{
return new Employee();
}
__declspec(dllexport) void employee_Free(Employee* e)
{
delete e;
}
__declspec(dllexport) void employee_SetFirstName(Employee* e, char* s)
{
e->SetFirstName(std::string(s));
}
__declspec(dllexport) void employee_SetLastName(Employee* e, char* s)
{
e->SetLastName(std::string(s));
}
__declspec(dllexport) int employee_GetFullName(Employee* e, char* buffer, int bufferLen)
{
std::string fullName = e->GetFullName();
if(buffer != 0)
strncpy(buffer, fullName.c_str(), bufferLen);
return fullName.length();
}
}
次に、C# 側で別の小さなラッパーを作成すると、このクラスが正常にマーシャリングされます。
特に C# へのマーシャリングの場合、別のオプションとして、C インターフェイスではなく COM インターフェイスをクラスに提供することができます。基本的には同じことですが、個別のラッパーを作成せずに COM サポートを C++ クラスに直接追加するための多くのヘルパー クラスと特別なコンパイラ サポートがあります。COM オブジェクトは、C# から直接参照できます。
しかし、それはiosでは役に立ちません...