CPUがx64だと言っても、まだまだx86の呼出し規則が優勢と言うかWOW64の方が全盛の内に、x86呼出し規則を浚ってみました。
C・C++間や他言語からのDLL内関数呼出しを行う場合、大事なのはCリンケージと __stdcall です。
特に指定しない限り、C++コンパイラはC++リンケージであるC++のタイプセーフな名前付け規約(名前装飾)と、C++の呼出し規則を使用します。
Cコンパイラは既定で、Cの呼出し規則とCリンケージを使います。
★C・C++間でDLL内の関数を利用するには、Cリンケージである extern "C" を指定して関数を宣言し、コンパイラがC++の関数名を装飾するのを禁止する必要があります。
★他言語からDLL内の関数を利用するには、__stdcall 呼出し規則を使用する必要があります。
<注意点>
・C++が装飾名を必須とするのは、オーバーロードされた関数、クラスや名前空間のメンバー、コンストラクターやデストラクター、などなどを一意に特定する必要がある事に起因します。
・__stdcall で修飾さる名前には、関数名の前にアンダースコア '_' が、後にアットマーク '@' が、更にその後に引数リストのバイト数が付けられます。例えば、int __stdcall func(int a, double b) 関数は、_func@12 と名前修飾されます。
【Visual C/C++ コンパイラの呼出し規則】
規約 *1 | コンパイラ オプション | スタック 一掃 | 引数渡し | Cリンケージ 修飾形式 *2 | 補足 |
---|---|---|---|---|---|
__cdecl | /Gd x86 のみ |
呼出し元 | 右から左の順で スタックに積まれる |
_fname | C/C++標準 |
__clrcall | なし | N/A | 左から右の順で CLR式スタックに収容 |
N/A | .NET専用 |
__stdcall | /Gz x86 のみ |
呼出し先 | 右から左の順で スタックに積まれる |
_fname@num | Win32API呼出し 他言語アプリからの 関数呼び出し |
__fastcall | /Gr x86 のみ |
呼出し先 | レジスタで渡され 残りは右から左の順で スタックに積まれる |
@fname@num | 通常、高速呼出し |
__thiscall | なし | 呼出し先 | 右から左の順でスタック に積まれ this ポインタは ECX レジスタに格納 |
N/A | C++メンバー関数 |
__vectorcall | /Gv x86, x64 |
呼出し先 | レジスタで渡され 残りは右から左の順で スタックに積まれる |
fname@@num | __fastcall より多くの レジスタで渡すか 既定の x64 呼出し *3 を使う |
*1:Visual C/C++ コンパイラでサポートされている呼び出し規約
*2:fname は関数名、num は引数リストのバイト数を表す
*3:x64ABIの既定では4レジスタ高速呼出しの呼び出し規則が使用される