現在、DLLとして配布されるWindows用のC++ライブラリを開発しています。私の目標は、バイナリの相互運用性を最大限にすることです。より正確には、my DLL=の関数は、DLLを再コンパイルすることなく、MSVC++とMinGWの複数のバージョンでコンパイルされたコードから使用可能でなければなりません。しかし、 cdecl
または stdcall
。
「Cの呼び出し規約は、コンパイラ間で同じであることが保証されている唯一のものです」などのステートメントを聞くことがありますが、これは「 cdecl
の解釈には、特に値を返す "。これは、特定のライブラリ開発者( libsndfile など)が、目に見える問題なしに、配布するDLLでC呼び出し規約を使用することを止めていないようです。
一方、stdcall
呼び出し規約は明確に定義されているようです。私が言ったことから、すべてのWindowsコンパイラーは基本的にそれに従う必要があります。これはWin32とCOMで使用される規則だからです。これは、Win32/COMがサポートされていないWindowsコンパイラはあまり役に立たないという前提に基づいています。フォーラムに投稿された多くのコードスニペットは、関数をstdcall
と宣言していますが、whyを明確に説明する1つの投稿を見つけることができないようです。
相反する情報が多すぎるため、実行する検索ごとに異なる答えが得られますが、この2つを判断するのにはあまり役立ちません。私は、なぜ一方を選択する必要があるのか(または2つが同等である理由)について、明確で詳細な議論のある説明を探しています。
この質問は「クラシック」関数だけでなく、仮想メンバー関数呼び出しにも適用されることに注意してください。ほとんどのクライアントコードはmy DLL through "interfaces"、純粋な仮想クラス(パターンに従う)例えば here および there )で説明します。
実際のテスト(DLLとアプリケーションをMSVC++とMinGWでコンパイルし、それらを混合する)を行ったところです。表示されているように、cdecl
呼び出し規約でより良い結果が得られました。
より具体的には、stdcall
の問題は、MSVC++が_extern "C"
_を使用している場合でもDLLエクスポートテーブルの名前をマングルすることです。たとえば、foo
は__foo@4
_。これは__declspec(dllexport)
を使用する場合にのみ発生し、DEFファイルを使用する場合は発生しませんが、DEFファイルはメンテナンスの面倒であり、使用したくありません。
MSVC++の名前のマングリングには、2つの問題があります。
GetProcAddress
を使用すると、少し複雑になります。foo@4
_ではなく__foo@4
_を使用するため、リンクが複雑になります)。また、「アンダースコアバージョン」と互換性のないDLLおよびアプリケーションの「アンダースコアバージョンでない」ポップアップが表示されるリスクが生じます。私はcdecl
規則を試しました:MSVC++とMinGWの相互運用性はそのままで完全に動作し、名前はDLLエクスポートテーブルで装飾されていません。仮想メソッドで機能します。
これらの理由から、cdecl
は私にとって明らかな勝者です。
2つの呼び出し規約の最大の違いは、 "_ _ cdecl"が呼び出し元で関数呼び出し後にスタックのバランスをとる負担をかけることです。これにより、可変量の引数を持つ関数が可能になります。 「__stdcall」規則は本質的に「単純」ですが、この点では柔軟性が低くなります。
また、マネージ言語ではデフォルトでstdcall規則が使用されるため、cdeclを使用する場合、P/Invokeを使用するユーザーは呼び出し規則を明示的に指定する必要があります。
したがって、すべての関数シグネチャが静的に定義される場合、cdeclではないにしても、おそらくstdcallに傾くでしょう。
セキュリティの観点から、__cdecl
規則は、スタックの割り当てを解除する必要がある呼び出し側であるため、「安全」です。 __stdcall
ライブラリは、開発者がスタックの割り当てを適切に解除するのを忘れている可能性がある、または攻撃者がDLLのスタックを破損して(たとえば、APIフックによって)コードを注入する可能性があります。私の直感が正しいことを示すCVSセキュリティの例はありません。