C言語で発生するエラーC3387について解説 ― __declspec(dllexport)/__declspec(dllimport)の正しい使用方法
C言語やC++の開発で、エラーC3387は__declspec(dllexport)や__declspec(dllimport)をマネージドやWinRT型のメンバーに適用した場合に発生します。
エラーメッセージの通り、これらの修飾子は対象外であるため、コードを見直して不要な記述を削除するか、代替手法を検討する必要があります。
サンプルコードで修正例が示されていますので、参考にして対応してください。
エラーC3387の概要
エラーC3387は、マネージド型やWinRT型のメンバーに対して__declspec(dllexport)
や__declspec(dllimport)
を適用しようとした場合に発生するコンパイルエラーです。
エラーが発生すると、指定したメンバーが不適切な対象であることが明記されます。
ここではエラーメッセージの内容や発生条件について詳しく解説します。
エラーC3387の定義と発生条件
エラーC3387は、主にC/C++においてマネージドコードやWindowsランタイム(WinRT)型に対して従来の__declspec
修飾子を適用するときに発生します。
以下の内容が発生条件に関わるポイントです。
エラーメッセージの分析
エラーメッセージには次のような記述が含まれています。
'member' : __declspec(dllexport)/__declspec(dllimport) は、マネージドまたは WinRT 型のメンバーに適用できません
このメッセージは、対象のメンバーがマネージド型またはWinRT型であるため、従来の__declspec
修飾子ではエクスポートやインポートが行えないことを示しています。
また、エラーメッセージにはエラー発生の理由として「対象型がサポート対象外」である旨が記され、対象コード部分の誤用が明確になります。
対象となるメンバーと対象外のメンバー
エラーC3387が発生するのは、マネージド型やWinRT型に分類されるメンバーだけです。
- 対象となるメンバー:
- マネージドクラスやrefクラスに属するメソッドやプロパティ
- WinRTクラスに定義されたメンバー
- 対象外のメンバー:
- ネイティブC/C++のクラスが持つメンバー
- 非マネージド型のグローバル関数や変数
したがって、ネイティブ側のコードでは__declspec(dllexport)
や__declspec(dllimport)
が正しく機能する一方で、マネージド側では別の実装手法を採用する必要があります。
関連する言語環境と制約
エラーC3387は、主にC++のマネージド拡張(/clrオプション)やWinRTプラットフォームで開発している際に遭遇します。
C言語自体ではこのエラーは一般的ではありませんが、両言語を組み合わせた環境でのビルド時に、対象となるコード部分で意図しない__declspec
修飾子が適用されると発生する可能性があります。
また、最新のコンパイラや開発環境では、マネージド型やWinRT型向けの他の設定や属性が推奨されるため、従来の__declspec
の使用は制限される場合があります。
__declspec修飾子の基本理解
__declspec
修飾子は、関数や変数、クラスに対してエクスポートやインポートの属性を付与するために使用されます。
これにより、モジュール間でのシンボルの共有が容易になります。
ただし、マネージド型やWinRT型の場合は別の手法が必要となります。
__declspec(dllexport)の役割
__declspec(dllexport)
は、関数や変数、あるいはクラスをDLLからエクスポートするために使用されます。
この修飾子を付与することによって、外部のアプリケーションやライブラリから対象シンボルにアクセスできるようになります。
例えば、ネイティブなC/C++コードでは次のように使用されます。
#include <stdio.h>
// グローバル関数をDLLからエクスポートする例
__declspec(dllexport) void printMessage() {
printf("Hello from DLL!\n");
}
int main() {
printMessage();
return 0;
}
Hello from DLL!
__declspec(dllimport)の機能
一方で、__declspec(dllimport)
は、他のDLLからシンボルをインポートするために使用されます。
これにより、リンク時にシンボルの解決が行われ、プログラムが正しく外部の関数や変数にアクセスすることが可能になります。
インポート側は、次のような記述が一般的です。
#include <stdio.h>
// dllimportを使用する場合の宣言例
__declspec(dllimport) void printMessage();
int main() {
printMessage();
return 0;
}
マネージド型およびWinRT型への適用制限
マネージド型やWinRT型は、.NET環境やWindowsのランタイム環境で使用される型であり、従来のネイティブC/C++コードとは異なる管理手法が採用されています。
そのため、これらの型のメンバーに対して__declspec(dllexport)
や__declspec(dllimport)
を適用すると、コンパイラはエラーC3387を出力します。
代替として、マネージドコードでは属性(例えばC#の[DllExport]
など)や専用のインターフェース、WinRTの場合はIDL(Interface Definition Language)を用いる方法が推奨されます。
エラー発生事例と原因分析
エラーC3387は、実際のコードで発生するケースを用いて原因を探ることができます。
ここでは、実際にエラーが発生するサンプルコードや注意すべきポイントについて解説します。
サンプルコードによるエラー再現
以下に、エラーC3387を再現するためのサンプルコードを示します。
このコードは、マネージド側の型(ref class
として定義)に対して不適切な修飾子__declspec(dllexport)
を適用しています。
コード内の誤用例
#include <stdio.h>
// managed codeでのref classの定義例
// /clrオプションでコンパイルする必要があります
ref class ManagedClass {
public:
// 不適切な修飾子の適用によりエラーC3387が発生する
void __declspec(dllexport) displayMessage() {
// メッセージを表示する処理
System::Console::WriteLine("ManagedClassからのメッセージ");
}
};
int main() {
ManagedClass^ instance = gcnew ManagedClass();
instance->displayMessage();
return 0;
}
// コンパイルエラー: 'displayMessage' : __declspec(dllexport)/__declspec(dllimport) は、マネージドまたは WinRT 型のメンバーに適用できません
エラー発生の背景
この誤用例では、マネージド型であるref class ManagedClass
のメンバー関数に__declspec(dllexport)
を適用しています。
そのため、コンパイラはネイティブコード向けの修飾子がマネージド環境で無効であると判断し、エラーC3387を出力します。
このエラーは、対象となる型が.NETランタイムにより特別な処理を受けているためであり、従来のエクスポート/インポートの仕組みが適用できない点を示しています。
開発環境における注意点
マネージドコードやWinRT型を使用する際は、以下の点に注意する必要があります。
- コンパイルオプションの設定:マネージドコードの場合、必ず
/clr
オプションを指定する必要があります。 - 適切な修飾子の使用:ネイティブコードと混在する場合は、マネージド対象かどうかをしっかり見極め、不要な修飾子の適用を避けることが重要です。
- プロジェクト設定の確認:プロジェクト設定で使用するランタイムやライブラリの互換性を確認し、誤った設定により予期しないエラーが発生しないようにしましょう。
エラー回避と修正方法
エラーC3387を回避するためには、マネージド型やWinRT型に対して不適切な__declspec
修飾子を適用しないことが必要です。
ここでは、不要な修飾子の除去や修正後のコード検証の手法について解説します。
不要な修飾子の除去方法
マネージド型やWinRT型のメンバーには、従来の__declspec(dllexport)
や__declspec(dllimport)
は不要です。
誤った修飾子を削除することでエラーは解消されます。
先ほどのサンプルコードの場合、修正すべき点は次のようになります。
#include <stdio.h>
// /clrオプションでコンパイルするmanaged codeの正しい定義例
ref class ManagedClass {
public:
// 修正: 不要な修飾子を削除
void displayMessage() {
// メッセージを表示する処理
System::Console::WriteLine("ManagedClassからのメッセージ");
}
};
int main() {
ManagedClass^ instance = gcnew ManagedClass();
instance->displayMessage();
return 0;
}
上記の修正により、エラーC3387は解消し、コードは正しくコンパイルされます。
改善後コードの検証手法
エラー回避のための修正が完了したら、コードの動作検証を行います。
以下に、検証手法のポイントを示します。
修正点のポイント解説
- 修正前と修正後のコードを比較し、不要な
__declspec
修飾子が完全に削除されていることを確認します。 - 修正前後でコンパイルオプション(特に
/clr
など)が一致しているかチェックしましょう。 - ビルドログやコンパイラの警告も再確認し、他の要因でエラーが出ていないかを検証します。
動作確認の手順
- 修正後のコードを保存します。
- 指定のコンパイルオプション(例:
/clr
)を用いてビルドを実行します。 - 実行ファイルが生成され、正しく動作するか確認します。
- 必要に応じて、デバッガを利用し、プログラムの流れや出力結果が期待通りであることを検証します。
C/C++環境での特有注意事項
C言語とC++間、またはネイティブコードとマネージドコードが混在するプロジェクトでは、細心の注意が必要です。
ここでは環境ごとの注意事項について説明します。
C言語とC++間の違いによる影響
C言語では、__declspec(dllexport)
や__declspec(dllimport)
は一般的に問題なく使用されるケースが多いですが、
C++では名前のマングリングや、クラス・メソッドごとに定義される特殊な属性対応が影響する可能性があります。
特に、C++でマネージド型を利用する場合、C言語の手法と同じ修飾子は用いられず、クラス全体や関数の振る舞いに違いが生じるため、コードの分離や適切なインターフェース設計が求められます。
開発ツールでの設定留意点
現代の開発ツールやビルドシステムでは、
- プロジェクト設定での
/clr
オプションの有無 - ネイティブコードとマネージドコードのリンク設定
- DLLエクスポート/インポートのための定義マクロの管理
などが重要になります。
IDEやビルドツールの設定によっては、デフォルトでマネージドコードが有効化されている場合もあるため、環境設定を再確認し、正しいライブラリやフラグが適用されているかどうかを常にチェックすることが大切です。
まとめ
本記事では、エラーC3387の概要や発生条件、エラーメッセージの意味、対象となるメンバーと対象外のメンバーについて解説しています。
また、__declspec(dllexport)
と__declspec(dllimport)
の役割や、マネージド型・WinRT型に対する適用制限について詳しく説明しました。
さらに、サンプルコードを用いて実際のエラー発生例と原因を示し、修正方法や動作確認の手順、C/C++環境特有の注意事項についても触れています。