__clrcall
関数がマネージド コードからのみ呼び出されることを指定します。 マネージド コードからのみ呼び出されるすべての仮想関数では __clrcall を使用します。 ただし、この呼び出し規則は、ネイティブ コードから呼び出される関数には使用できません。 __clrcall 修飾子は Microsoft 固有です。
マネージド関数から仮想マネージド関数を呼び出す場合、またはマネージド関数からポインターを使用してマネージド関数を呼び出す場合は、__clrcall を使用してパフォーマンスを向上させます。
エントリ ポイントはコンパイラが生成した個別の関数です。 関数にネイティブ エントリ ポイントとマネージド エントリ ポイントがある場合、そのいずれかが、関数実装を持つ実際の関数になります。 もう一方の関数は実際の関数に呼び出す別の関数 (サンク) で、共通言語ランタイムに PInvoke を実行させます。 関数を __clrcall としてマークすると、関数の実装が MSIL である必要があり、ネイティブ エントリ ポイント関数が生成されないことを示します。
__clrcall が指定されていない場合にネイティブ関数のアドレスを取得すると、コンパイラはネイティブ エントリ ポイントを使用します。 __clrcall は、関数が管理され、マネージド コードからネイティブへの遷移を行う必要がないことを示します。 その場合、コンパイラはマネージド エントリ ポイントを使用します。
(/clr:pure
または /clr:safe
ではなく) /clr
を使用し、__clrcall を使用しない場合は、関数のアドレスを取得すると常にネイティブ エントリ ポイント関数のアドレスが返ります。 __clrcall を使用すると、ネイティブ エントリ ポイント関数が作成されないため、エントリ ポイント サンク関数ではなくマネージド関数のアドレスを取得します。 詳細については、ダブル サンキングに関するページを参照してください /clr:pure および /clr:safe コンパイラ オプションは Visual Studio 2015 では非推奨とされており、Visual Studio 2017 ではサポートされていません。
/clr (共通言語ランタイムのコンパイル) は、すべての関数および関数ポインターが __clrcall であり、コンパイラがコンパイル単位内の関数が __clrcall 以外にマークされることを許可しないことを意味します。 /clr:pure を使用するときは、__clrcall は関数ポインターと外部宣言でのみ指定できます。
この関数が MSIL を実装している限り、/clr を使用してコンパイルされた既存の C++ コードから __clrcall 関数を直接呼び出すことができます。 __clrcall 関数は、インライン asm を持つ関数から直接呼び出したり、CPU 固有の組み込みを呼び出したりすることはできません (たとえば、それらの関数が /clr
でコンパイルされている場合でも)。
__clrcall 関数ポインターだけが、作成されたアプリケーション ドメインで使用されるようになっています。 アプリケーション ドメインを越えて __clrcall 関数ポインターを渡すのではなく、CrossAppDomainDelegate を使用します。 詳細については、「アプリケーション ドメインと Visual C++」を参照してください。
例
関数が __clrcall で宣言されている場合、必要に応じて (関数が呼び出された場合など) コードが生成されることに注意してください。
// clrcall2.cpp
// compile with: /clr
using namespace System;
int __clrcall Func1() {
Console::WriteLine("in Func1");
return 0;
}
// Func1 hasn't been used at this point (code has not been generated),
// so runtime returns the adddress of a stub to the function
int (__clrcall *pf)() = &Func1;
// code calls the function, code generated at difference address
int i = pf(); // comment this line and comparison will pass
int main() {
if (&Func1 == pf)
Console::WriteLine("&Func1 == pf, comparison succeeds");
else
Console::WriteLine("&Func1 != pf, comparison fails");
// even though comparison fails, stub and function call are correct
pf();
Func1();
}
in Func1
&Func1 != pf, comparison fails
in Func1
in Func1
次のサンプルでは、関数ポインターがマネージド コードからのみ呼び出されるように、関数ポインターを定義する方法を示します。 これにより、コンパイラはマネージド関数を直接呼び出し、ネイティブ エントリ ポイント (ダブル サンクの問題) を避けることができます。
// clrcall3.cpp
// compile with: /clr
void Test() {
System::Console::WriteLine("in Test");
}
int main() {
void (*pTest)() = &Test;
(*pTest)();
void (__clrcall *pTest2)() = &Test;
(*pTest2)();
}