• ベストアンサー

Cの関数をC++とCのどちらからでも呼べるようにするには?

お世話になります。 環境はVC6.0となります。 C++で作成したプログラムから、Cで作成したDLLを呼び出す場合、DLL側のプロトタイプ宣言に「extern "C"」を付加する必要があると認識していますが、 呼び出し側がC++かC言語が分からない場合、Cで作成するDLLにはどのように記述するのが一般的(標準的)でしょうか? ちなみに調べてみたところ、 以下のように、「__cplusplus」でくくる方法もあるようですが、C++の標準仕様ではないとの記載がありました。 #ifdef __cplusplus extern "C" { #endif int function(int num); int function2(int num); int function3(int num); #ifdef __cplusplus } #endif よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

DLLプロジェクトに*.defファイルを追加し、 その内容にエクスポート関数名を指定するのが、 最も簡単で、一般的です。 ====MyDll.def=== LIBRARY MyDll EXPORTS function @1 function2 @2 function3 @3 ================ MyDll.lib(エクスポートライブラリ)をリンクす場合は インポート用ヘッダーでのプロトタイプ宣言を __declspec( dllimport)で修飾します。 実際にやってみて、細かい点を確認してみてください。

MUTOUSE
質問者

お礼

ご回答ありがとうございました。 実はDEFファイルを使用してエクスポートはしていましたが、 C++の呼び出し側プログラムで「__declspec( dllimport) 」は使用することは知りませんでした。。。 結局、DEFファイルはそのままに、DLLのヘッダファイルにあるプロトタイプ宣言を以下の記述をしました。 #ifndef _EXPORTING //呼び出し側の宣言 __declspec(dllexport) int func1(); #else //DLL側の宣言 int func1(); #endif

その他の回答 (1)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

> 以下のように、「__cplusplus」でくくる方法もあるようですが、C++の標準仕様ではないとの記載がありました。 __cplusplusマクロはC++の標準仕様です。

MUTOUSE
質問者

お礼

ご回答ありがとうございます。 > __cplusplusマクロはC++の標準仕様です。 そのようですね。どこかのHPに書いていたのを鵜呑みにしていました。。。 今ひとつハッキリしないのですが、 MSDNに下記のサンプルがあり、「C で記述された DLL 内の関数に C/C++ 言語のモジュールからアクセスするには、__cplusplus プリプロセッサ マクロを使う」と明記されているのですが、 #ifdef __cplusplus extern "C" { // only need to export C interface if // used by C++ source code #endif __declspec( dllimport ) void MyCFunc(); __declspec( dllimport ) void AnotherCFunc(); #ifdef __cplusplus } #endif 試してみたところ、No.1さんの回答のとおり「__declspec( dllimport )」をC++の呼び出し側に取り込むだけで「extern "C"」を記述しなくても動作しました。 import / export と C++ / C は話がズレているかもしれませんが。。。

関連するQ&A

  • C#から、C++作成dll内の関数を呼び出す方法

    C#から、C++で作成したdll内の関数を呼び出す方法は、以下の方法で実現できました。(メッセージボックスで "10" が表示されました) [C++側のヘッダファイル] #ifdef CPPDLL_EXPORTS #define CPPDLL_API __declspec(dllexport) #else #define CPPDLL_API __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ CPPDLL_API int fnCppDll(); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ [C++側のソースファイル] CPPDLL_API int fnCppDll() { return 10; } [C#側] using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsFormsApp1 { public partial class Form1 : Form { [DllImport("CppDll.dll")] public static extern int fnCppDll(); public Form1() { InitializeComponent(); } private void buttonGo_Click(object sender, EventArgs e) { int n = fnCppDll(); MessageBox.Show(n.ToString()); } } } 今回お聞きしたいのは、int型ではなく、C++側で型を定義されたクラスのオブジェクト(の参照)を返す関数を、C#側から呼び出し、それをどうやってC#で受け取るかを教えていただきたいのです。 具体的には以下のように実装してみました。 [C++側のヘッダファイル] #ifdef CPPDLL_EXPORTS #define CPPDLL_API __declspec(dllexport) #else #define CPPDLL_API __declspec(dllimport) #endif class CPPDLL_API CCppDll { public: int data; CCppDll(void); }; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ CPPDLL_API CCppDll& fnCppDll(); // C#が参照渡しということで、参照を返すようにした。 #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ [C++側のソースファイル] CPPDLL_API CCppDll& fnCppDll() { CCppDll* a = new CCppDll(); return *a; } CCppDll::CCppDll() : data(11) // メンバーは 11 で初期化 { } [C#側] using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsFormsApp1 { // ビルドエラーが起きるないように、とりあえず、C++と同じ(ような)クラスを定義 public class CCppDll { public int data; } public partial class Form1 : Form { [DllImport("CppDll.dll")] public static extern CCppDll fnCppDll(); public Form1() { InitializeComponent(); } private void buttonGo_Click(object sender, EventArgs e) { int n; CCppDll cls = fnCppDll(); // ※ n = cls.data; MessageBox.Show(n.ToString()); } private void buttonCancel_Click(object sender, EventArgs e) { Close(); } } } 実装しながらも、「これじゃあ、ダメだろうな。いかにもダメだな」と思った通り、上の※の部分で以下の例外が発生しました。 ------- 例外(ここから) ------- マネージド デバッグ アシスタント 'FatalExecutionEngineError' : 'ランタイムの重大なエラーが発生しました。エラーのアドレスは 0xcc9ff5a2、スレッド 0x36c8 です。エラー コードは 0xc0000005 です。これは CLR のバグであるか、またはユーザー コードのアンセーフまたは確認不可能な部分にバグがある可能性があります。このバグの一般的な原因には、スタックが壊れる可能性のある COM-interop または PInvoke のユーザー マーシャリング エラーが含まれています。' ------- 例外(ここまで) ------- 明らかに基本的なことが分かっていないことから起因するエラーと思われますが、具体的にどう実装すれば、正常に動きますか。(メッセージボックスで "11" を表示) よろしくお願いします。

  • ライブラリで宣言した構造体が認識されない

    ライブラリのヘッダファイル complex.h #ifndef COMPLEX_H #define COMPLEX_H #ifdef __cplusplus extern "C"{ #endif typedef struct{ double real,imaginary; } COMPLEX; extern int complex_error_code; extern COMPLEX add_complex(COMPLEX a ,COMPLEX b); #ifdef __cplusplus } #endif #endif ライブラリのソースファイル complex.c #include <stdio.h> #include <math.h> #include "complex.h" int complex_error_code = 0; COMPLEX add_complex(COMPLEX a ,COMPLEX b) { COMPLEX c; c.real=a.real+b.real; c.imaginary=a.imaginary+b.imaginary; return c; } 動作確認のアプリケーションファイル test_complex.c #include <stdio.h> int main(void) { COMPLEX z,z1,z2; z1={1.0,1.0}; z2={2.0,2.0}; z=add_complex(z1,z2); printf("%lf+%lfi",z.real,z.imaginary); return 0; } COMPLEX undeclared (first use in this function) というエラーメッセージが出て、宣言したはずの構造体COMPLEXが認識されていないようなのですが、理由がわかりません。 よろしくお願いします。

  • DLLでLIBファイルが作成されない

    DLLを作るプログラムをしているのですが、どうしてもコンパイル時にLIBファイルが作成されません。 1>warning C4091: '__declspec(dllexport)' : 変数が何も宣言されていないときは、'DDDClass' の左辺を無視します。 と警告が出てしまいます。 私が書いたソースは以下の通りで、クラスとそのメンバをEXPORTしたいのです。ここで、最後のコメントを外して有効にするとLIBファイルが作成されることはわかっています。また、クラスのメンバにEXPORTをつけてもオーバーライトだと怒られてしまいます。 どのようにしたらLIBファイルが作成されるようになるのでしょうか? ■■■stub.h■■■ #ifdef __cplusplus #define EXPORT extern "C" __declspec (dllexport) #else #define EXPORT __declspec (dllexport) #endif typedef int (*HOGEHOGE)(int i); EXPORT class DDDClass{ private: int num; HOGEHOGE c; public: DDDClass(); int CALLBACK counter(); int CALLBACK touroku(int (*b)(int i)); }; ■■■stub.cpp■■■ #include <windows.h> #include "Stub1.h" int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved){ return true; } EXPORT class DDDClass; DDDClass::DDDClass(){ num =0; c = NULL; } int CALLBACK DDDClass::touroku(int (*b)(int i)){ c = b; return 0; } int CALLBACK DDDClass::counter(){ //c = b; c(num); num++; return 0; } /* EXPORT bool CALLBACK aaaaa(){ return true; } */

  • シンボルをエクスポートするDLLの初歩的トラブル

    VC++6.0で簡単なDLL作成にチャレンジしたところ、ソースファイルの最後のところで、エンドオブファイルのエラーが出ます。教本と同じように書いたつもりですが、何処が異なっているのか分かりません。エラーを出す原因となっている箇所を御指摘下さい。 ↓ソースファイル #include "stdafx.h" #include "SUB.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } SUB_API LONG _stdcall subtract( LONG sub1 , LONG sub2 ) { return( sub1 - sub2 ); } ↓ここからはヘッダーファイル #ifdef SUB_EXPORTS #define SUB_API __declspec(dllexport) #else #define SUB_API __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif SUB_API LONG __stdcall subtract( LONG sub1, LONG sub2 ); #ifdef _cplusplus } #endif --------------------構成: SUB - Win32 Debug-------------------- コンパイル中... StdAfx.cpp コンパイル中... SUB.cpp C:\Program Files\Microsoft Visual Studio\MyProjects\SUB\SUB.cpp(38) : error C2059: 構文エラー : 'end of file' cl.exe の実行エラー SUB.dll - エラー 1、警告 0

  • C言語で、記憶クラス指定子extern・staticを関数に指定

    C言語の本に、「関数の定義と呼び出す側が別ソースファイルの場合、プロトタイプはヘッダーファイルに書き、定義側と呼び出し側の両方でインクルードしましょう」ということが書かれていました。 例えば、 ===code1a.c=== extern void funcB(int); static void funcA() { funcB(1); } ===code1b.c=== void funcB(int a) { printf("%d\n",a); } このような場合には、もしcode1b.cの中の関数funcBに引数を追加した場合、再コンパイルしても気づかないのでよくない。 そこで、次のようにヘッダーファイルを作り、プロトタイプはそこに書くべきだ。 ***code2b.h*** extern void funcB(int); ***code2a.c*** #include "code2b.h" static void funcA() { funcB(1); } ******code2b.c**** #include "code2b.h" void funcB(int a) { printf("%d\n",a); } 記述は以上のようなことです。 #include "code2b.h" とは、 extern void funcB(int); が書いてあるのと同じだと思います。 私が思ったのは、本の勧める方法では、 funcBを定義しているcode2b.cで、プロトタイプの記憶クラス指定子が、externになっているが良いのか(externとは、別のソースファイルで定義されているという意味ではないか)ということです。 extern, staticは、プロトタイプに書くべきなのか、関数の定義に書くべきなのか、も両方に書くべきなのでしょうか。 私の処理系では、 ・プロトタイプ宣言でexternを付けて関数定義でstaticを付ける、 ・staticを付けた関数を他のソースファイルで呼ぶ、 などの明らかに矛盾する場合は、コンパイルエラーになります。 でも、extern単独での役割はなさそうです。 他の処理系でも同じでしょうか。 (main等省略)

  • AKI-h8 3069f C言語 HEW マイコン DIPスイッチで LED ON_OFF プログラム 「組込みI/O制御演習」

    こんにちは。 標記開発環境でマイコンを制御しています。 下記ソースでDIPスイッチのONOFFでLEDを点灯させたいのですが うまくいきません。 多分 P4DR.BIT.B0 =~P5DR.BIT.B0; の部分の修正が必要かと思うのですが。 どなたかご存知の方よろしくお願いします。 //------------------ #include "iodefine.h" void main(void); #ifdef __cplusplus extern "C" { void abort(void); } #endif void main(void) { P5DDR = 0; P5PCR.BYTE = 0xff; P4DDR = 0xff; while(1) { P4DR.BIT.B0 =~P5DR.BIT.B0; } } #ifdef __cplusplus void abort(void) { } #endif

  • C/C++の型について

    Windows2000で、VC++6で型のサイズを調べると、以下のとおりでした。 int 4バイト long 4バイト long int 4バイト 環境はWindows2000, CPUはCeleron1200Mhzです。 環境やコンパイラによって型のサイズや呼び方も変わるのでしょうか? VisualBasic6用のDLLをVCで作成した記憶があるのですが、そのときは VB6のInteger型が2バイトだったので C(Ver7.0?????)側も2バイトだったと思います。 今VB6のInteger型にあわせる場合はC側ではshortでないとおかしくなるのでしょうか? 私はブランクがあり、そのあたりの事情がよくわかっていません。 あるサイトのページを見ると intが2バイトで、 long intは同じ4バイトで long long intという型もありました。VC6++では long long intは無いようです。 http://seclan.dll.jp/c99d/c99d05.htm VC2005++やGCCでは8バイトの型はあるのでしょうか?

  • Cで作成したDLLからC++内で関数を利用する。

    こんにちは。 Cで作成したDLLをC++で呼び出してその関数を使いたいのですが、リンクがうまくいきません。 externを使えばいいのですか?? 具体的な使い方が分からないので、教えてください。

  • C++の組込みマクロのassert()が使えない

    こんにちは。 CygwinでC++開発を行っているのですが、C++の関数型マクロであるassert()が使えなくて困っています。 main関数を含むソースファイルに、 #include assert.h を記述しておけば使えるはずなのですが、そのソースファイルから実行ファイルを、 g++コマンドでビルドしようとすると、以下のようなエラーが出ます。 ------------------------------------------------------------ $ g++ test03-STLの使用.cpp /cygdrive/c/Users/Kei/AppData/Local/Temp/cckwDP4e.o:test03-STLの使用.cpp:(.text+0x904): undefined reference to `___assert_func' collect2: ld はステータス 1 で終了しました ------------------------------------------------------------ test03-STLの使用.cpp のmain関数は、以下の通りです。 ------------------------------------------------------------ int main() { string s="ABC DEF\nGH\tIJ"; reverse(s.begin(), s.end() ); assert(s=="JI\tHG\nFED CBA"); cout<<s return 0; } ------------------------------------------------------------ ちなみに、実際にインクルードされる /usr/include/assert.h の内容は以下のようになっていました。 ------------------------------------------------------------ /* assert.h */ #ifdef __cplusplus extern "C" { #endif #include "_ansi.h" #undef assert #ifdef NDEBUG /* required by ANSI standard */ # define assert(__e) ((void)0) #else # define assert(__e) ((__e) ? (void)0 : __assert_func (__FILE__, __LINE__, \ __ASSERT_FUNC, #__e)) # ifndef __ASSERT_FUNC /* Use g++'s demangled names in C++. */ # if defined __cplusplus && defined __GNUC__ # define __ASSERT_FUNC __PRETTY_FUNCTION__ /* C99 requires the use of __func__. */ # elif __STDC_VERSION__ >= 199901L # define __ASSERT_FUNC __func__ /* Older versions of gcc don't have __func__ but can use __FUNCTION__. */ # elif __GNUC__ >= 2 # define __ASSERT_FUNC __FUNCTION__ /* failed to detect __func__ support. */ # else # define __ASSERT_FUNC ((char *) 0) # endif # endif /* !__ASSERT_FUNC */ #endif /* !NDEBUG */ void _EXFUN(__assert, (const char *, int, const char *) _ATTRIBUTE ((__noreturn__))); void _EXFUN(__assert_func, (const char *, int, const char *, const char *) _ATTRIBUTE ((__noreturn__))); #ifdef __cplusplus } #endif ------------------------------------------------------------ Borland C++ Compilerのbcc32コマンドでは、先ほどのソースファイルから実行ファイルをビルドすることができたので、何が問題なのかが分かりません。 以上の件について何かご存知の方がいらっしゃれば、是非教えて頂きたいと思います。 では、よろしくお願い致します。

  • VC++2010 EE で #ifdef _MSC_VER #endi

    VC++2010 EE で #ifdef _MSC_VER #endif でエラー ソース始まり // _msc_ver.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #ifdef _MSC_VER #include "stdafx.h" #endif int _tmain(int argc, _TCHAR* argv[]) { return 0; } ソース終わり > fatal error C1020: 予期しない #endif です。 と出ます。なぜでしょうか?