#navi_header|C言語系| CLR, .NET, C++ は使わずに、C言語とCRT(Cランタイムライブラリ)とWin32APIだけを用いて、コンソール/Windows/スタティックライブラリ/DLLを、コマンドラインからコンパイルして作成する方法のまとめ。UNICODE対応は使わない。 ※複数ファイルの分割コンパイル+リンクについては [[583]] 参照。 参考MSDN(Express Edition): - 「Visual C++」→「概要」→「Visual C++ ガイドツアー」 対象:Visual C++ 2008 Express Edition > cl Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. > link Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. #more|| #outline|| ---- * コンソールアプリ win32con.c: #pre||> #include int main() { printf("Hello, World!\n"); return 0; } ||< コンパイル&実行: #pre||> > cl /D "WIN32" /D "_CONSOLE" win32con.c Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. win32con.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:win32con.exe win32con.obj > win32con.exe Hello, World! ||< ポイント: - VC++のテンプレートに従い、 ''WIN32'' と ''_CONSOLE'' をコマンドラインから定義してみた。ヘッダーファイルなどで使うとよいかも。 - デバッグ時の処理を"#ifdef"などで切り替えたい場合は、 ''_DEBUG'' をコマンドラインから定義し、それを見るようにする。テンプレートでも、デバッグモードだと"_DEBUG"が定義される。 - UNICODE対応したい場合は ''UNICODE'' , ''_UNICODE'' を定義し、tchar.hや汎用テキストのマッピング対応などを行う。 -- ヘッダーやソース中でASCII/MBCS/UNICODEを切り分けたい場合に使うと良いかも。 VC++のWin32コンソールアプリケーションのテンプレートで使われている"/D": /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" * Windowsアプリ win32win.c: #pre||> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBoxA( NULL, "Hello, Win32 GUI Applications!", "Hello, World!", MB_OK); return 0; } ||< コンパイル&実行: #pre||> > cl /D "WIN32" /D "_WINDOWS" win32win.c user32.lib Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. win32win.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:win32win.exe win32win.obj user32.lib > win32win.exe (メッセージボックス表示) ||< ポイント: - VC++のテンプレートに従い、 ''WIN32'' と ''_WINDOWS'' をコマンドラインから定義してみた。ヘッダーファイルなどで使うとよいかも。 - デバッグ時の処理を"#ifdef"などで切り替えたい場合は、 ''_DEBUG'' をコマンドラインから定義し、それを見るようにする。テンプレートでも、デバッグモードだと"_DEBUG"が定義される。 - UNICODE対応したい場合は ''UNICODE'' , ''_UNICODE'' を定義し、tchar.hや汎用テキストのマッピング対応などを行う。 -- ヘッダーやソース中でASCII/MBCS/UNICODEを切り分けたい場合に使うと良いかも。 VC++のWin32Windowsアプリケーションのテンプレートで使われている"/D": /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" ポイントその2: - MessageBox(A|W)を使っているので、インポートライブラリ"user32.lib"を指定している。これはリンカが用いる。 - 使っているWin32API関数のインポートライブラリを調べるには、当該関数のMSDNリファレンスを参照し、「対応情報」セクションを見ると「インポートライブラリ:xxxx.lib」として載っている。 - 複数ファイルに分割する場合、最後のLINKコマンドでインポートライブラリを指定すればよい。 分割コンパイルで、リンク時にインポートライブラリを指定する: コンパイル: > cl /D "WIN32" /D "_WINDOWS" /nologo /c win32win.c リンク: > link /nologo win32win.obj user32.lib * スタティックライブラリ サンプル: usestatic.c lib/ foo.c bar.c 参考MSDN(Express Edition): - 「Visual C++」→「C/C++ プログラムのビルド」→「C と C++ のビルド ツール」→「LIB リファレンス」 ** LIBファイルのビルド lib/foo.c: #pre||> int foo(int a, int b) { return a + b; } ||< lib/bar.c: #pre||> int bar(int a, int b) { return a * b; } ||< コンパイル: #pre||> (...)\lib> cl /D "WIN32" /D "_LIB" /c /nologo foo.c foo.c (...)\lib> cl /D "WIN32" /D "_LIB" /c /nologo bar.c bar.c (...)\lib> dir /B *.obj bar.obj foo.obj ||< ライブラリに結合: #pre||> (...)\lib> lib /out:libfoobar.lib foo.obj bar.obj Microsoft (R) Library Manager Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. (...)\lib> dir /b *.lib libfoobar.lib ||< LIBファイルに含まれるオブジェクト(COFF)ファイルの確認: #pre||> (...)\lib>lib /list libfoobar.lib Microsoft (R) Library Manager Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. foo.obj bar.obj ||< ポイント: - VC++のテンプレートに従い、 ''WIN32'' と ''_LIB'' をコマンドラインから定義してみた。ヘッダーファイルなどで使うとよいかも。 - デバッグ時の処理を"#ifdef"などで切り替えたい場合は、 ''_DEBUG'' をコマンドラインから定義し、それを見るようにする。テンプレートでも、デバッグモードだと"_DEBUG"が定義される。 - UNICODE対応したい場合は ''UNICODE'' , ''_UNICODE'' を定義し、tchar.hや汎用テキストのマッピング対応などを行う。 -- ヘッダーやソース中でASCII/MBCS/UNICODEを切り分けたい場合に使うと良いかも。 VC++のスタティックライブラリのテンプレートで使われている"/D": /D "WIN32" /D "_DEBUG" /D "_LIB" /D "_UNICODE" /D "UNICODE" ** クライアント側アプリでLIBファイルをリンク usestatic.c: #pre||> #include extern int foo(int a, int b); extern int bar(int a, int b); int main() { printf("foo(2, 3) = %d\n", foo(2, 3)); printf("bar(2, 3) = %d\n", bar(2, 3)); return 0; } ||< コンパイル&実行:("/D"オプションは省略) #pre||> > cl usestatic.c lib\libfoobar.lib Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. usestatic.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:usestatic.exe usestatic.obj lib\libfoobar.lib > usestatic.exe foo(2, 3) = 5 bar(2, 3) = 6 ||< ポイント: - clコマンドの段階でLIBファイルを指定すれば、自動的にリンクしてくれる。 - 複数ファイルに分割する場合、最後のLINKコマンドでLIBファイルを指定すればよい。 * DLL C言語の関数をDLLで公開し、C言語のコンソール/Windowsアプリケーションから使う簡単なサンプルで、コマンドライン上のツールを使ってDLLとそれを利用するアプリをビルドする基本的なパターンをまとめる。 ※DLLは、C++やMFC, VB側からの呼び出しなど幅広く関連しています。より深くDLLを理解し、使いこなすには、ぜひMSDNを参照して下さい。 参考MSDN(Express Edition): - 「Visual C++」→「一般的なプログラミング手順」→「DLL」 ※またこの記事では関数のみをDLLで公開します。データを公開したりアプリケーション/DLL間で共有する場合は以下のMSDNを参照して下さい。 参考MSDN(Express Edition): - 「Visual C++」→「一般的なプログラミング手順」→「DLL」→「DLL に関してよく寄せられる質問 」 -- →「DLL からデータをエクスポートする方法」 -- →「DLL 内のデータをアプリケーションまたはほかの DLL と共有する方法」 ** DLL側のビルド例1(ファイル分割無し, CLコマンドでDLLまで一気に生成) CLコマンドに"/LD"オプションを指定することで、ファイル分割無しの場合にDLLまで一気に生成するサンプル。 libdllfoobarbaz.c: #pre||> int __declspec(dllexport) foo(int a, int b) { return a + b; } int __declspec(dllexport) bar(int a, int b) { return a * b; } int baz(int a, int b) { return a - b; } ||< コンパイル: #pre||> > cl /LD libdllfoobarbaz.c Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. libdllfoobarbaz.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:libdllfoobarbaz.dll /dll /implib:libdllfoobarbaz.lib libdllfoobarbaz.obj ライブラリ libdllfoobarbaz.lib とオブジェクト libdllfoobarbaz.exp を作成中 > dir /b libdllfoobarbaz.* libdllfoobarbaz.c libdllfoobarbaz.dll libdllfoobarbaz.exp libdllfoobarbaz.lib libdllfoobarbaz.obj ||< エクスポートファイル(.exp)、インポートライブラリ(.lib)が同時に生成される。 DUMPBINでエクスポート領域を確認してみる: #pre||> > dumpbin /exports libdllfoobarbaz.dll Microsoft (R) COFF/PE Dumper Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file libdllfoobarbaz.dll File Type: DLL Section contains the following exports for libdllfoobarbaz.dll 00000000 characteristics 4B76BBEA time date stamp Sat Feb 13 23:49:14 2010 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001010 bar 2 1 00001000 foo Summary 2000 .data 2000 .rdata 1000 .reloc 7000 .text ||< "__declspec(dllexport)"が指定されたfoo(),bar()関数がエクスポートされ、baz()関数はエクスポートされていないことを確認出来る。 ポイント: - "/LD"だとデバッグ情報無し。"/LDd"でデバッグ用のDLLを生成する。 - 出力ファイル名を指定したい場合は "/Fe" オプションで指定する。 VC++のテンプレートで使われている定義については、次のファイル分割有りのパターンで示す。 ** DLL側のビルド例2(ファイル分割, CLコンパイル+LINKでDLL生成) libdllfoo.c: #pre||> int __declspec(dllexport) foo(int a, int b) { return a + b; } ||< libdllbar.c: #pre||> int __declspec(dllexport) bar(int a, int b) { return a * b; } ||< libdllbaz.c: #pre||> int baz(int a, int b) { return a - b; } ||< コンパイル: #pre||> > cl /c /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "_WINDLL" /nologo libdllfoo.c libdllfoo.c > cl /c /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "_WINDLL" /nologo libdllbar.c libdllbar.c > cl /c /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "_WINDLL" /nologo libdllbaz.c libdllbaz.c > dir /b *.obj libdllbar.obj libdllbaz.obj libdllfoo.obj ||< ポイント: - VC++のテンプレートに従い、 ''WIN32'', ''_WINDOWS'', ''_USRDLL'', ''_WINDLL'' をコマンドラインから定義してみた。ヘッダーファイルなどで使うとよいかも。 - デバッグ時の処理を"#ifdef"などで切り替えたい場合は、 ''_DEBUG'' をコマンドラインから定義し、それを見るようにする。テンプレートでも、デバッグモードだと"_DEBUG"が定義される。 - UNICODE対応したい場合は ''UNICODE'' , ''_UNICODE'' を定義し、tchar.hや汎用テキストのマッピング対応などを行う。 -- ヘッダーやソース中でASCII/MBCS/UNICODEを切り分けたい場合に使うと良いかも。 - VC++のテンプレートでは、"DLL名" + "_EXPORTS"というマクロもコンパイラオプションから定義される。ヘッダーファイルなどで使うと良いかも。 VC++のWin32DLLのテンプレートで使われている"/D": /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "(XXYY)DLL_EXPORTS" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" リンク: #pre||> > link /OUT:libdllfoobar.dll /DLL libdllfoo.obj libdllbar.obj libdllbaz.obj Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. ライブラリ libdllfoobar.lib とオブジェクト libdllfoobar.exp を作成中 > dir /b libdllfoobar.lib libdllfoobar.exp libdllfoobar.lib libdllfoobar.exp ||< ポイント: - リンカオプションで "/DLL" を指定してDLLを生成。 - "/OUT"オプションでDLLファイル名を指定。 - VC++のテンプレートでは "/SUBSYSTEM:WINDOWS" も指定されていたが、省略。 -- dumpbinで確認したところ、省略しても"subsystem"が"2", "Windows GUI"になっていたので大丈夫かも? ** 暗黙的なリンク(implicit link)を使ったアプリ側のビルド DLLを使うアプリ側で、インポートライブラリを使った暗黙的なリンクを行うサンプル。 usedll_implicit.c: DLLから呼ぶ関数を "__declspec(import)" を指定して宣言する。 #pre||> #include int __declspec(dllimport) foo(int a, int b); int __declspec(dllimport) bar(int a, int b); int main() { printf("foo(2, 3) = %d\n", foo(2, 3)); printf("bar(2, 3) = %d\n", bar(2, 3)); return 0; } ||< コンパイル&実行:DLLをビルドした時に生成されたインポートライブラリを一緒に指定する。 #pre||> > cl usedll_implicit.c libdllfoobar.lib Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. usedll_implicit.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:usedll_implicit.exe usedll_implicit.obj libdllfoobar.lib > usedll_implicit.exe foo(2, 3) = 5 bar(2, 3) = 6 ||< ** 明示的なリンク(explicit link)を使ったアプリ側のビルド 手動でDLLをロードし、関数ポインタを取得するサンプル。 usedll_explicit.c: #pre||> #include #include typedef int (CALLBACK* lp_foo)(int, int); typedef int (CALLBACK* lp_bar)(int, int); int main() { HINSTANCE hDll; lp_foo foo; lp_bar bar; // DLL名を指定してロード hDll = LoadLibrary("libdllfoobar"); if (NULL == hDll) { printf("dll load error.\n"); return 1; } // "foo()"関数のポインタを取得 foo = (lp_foo)GetProcAddress(hDll, "foo"); if (!foo) { printf("GetProcAddress error(foo).\n"); FreeLibrary(hDll); return 1; } // "bar()"関数のポインタを取得 bar = (lp_foo)GetProcAddress(hDll, "bar"); if (!bar) { printf("GetProcAddress error(bar).\n"); FreeLibrary(hDll); return 1; } printf("foo(2, 3) = %d\n", foo(2, 3)); printf("bar(2, 3) = %d\n", bar(2, 3)); return 0; } ||< コンパイル&実行: #pre||> > cl usedll_explicit.c Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. usedll_explicit.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:usedll_explicit.exe usedll_explicit.obj > usedll_explicit.exe foo(2, 3) = 5 bar(2, 3) = 6 ||< #navi_footer|C言語系|