CLR, .NET, C++ は使わずに、C言語とCRT(Cランタイムライブラリ)とWin32APIだけを用いて、コンソール/Windows/スタティックライブラリ/DLLを、コマンドラインからコンパイルして作成する方法のまとめ。UNICODE対応は使わない。
※複数ファイルの分割コンパイル+リンクについては C言語系/memos/VC++/01, 共通系コンパイルオプション 参照。
参考MSDN(Express Edition):
対象: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.
win32con.c:
#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
コンパイル&実行:
> 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コンソールアプリケーションのテンプレートで使われている"/D":
/D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE"
win32win.c:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBoxA( NULL, "Hello, Win32 GUI Applications!", "Hello, World!", MB_OK); return 0; }
コンパイル&実行:
> 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++のWin32Windowsアプリケーションのテンプレートで使われている"/D":
/D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE"
ポイントその2:
分割コンパイルで、リンク時にインポートライブラリを指定する:
コンパイル: > 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):
lib/foo.c:
int foo(int a, int b) { return a + b; }
lib/bar.c:
int bar(int a, int b) { return a * b; }
コンパイル:
(...)\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
ライブラリに結合:
(...)\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)ファイルの確認:
(...)\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++のスタティックライブラリのテンプレートで使われている"/D":
/D "WIN32" /D "_DEBUG" /D "_LIB" /D "_UNICODE" /D "UNICODE"
usestatic.c:
#include <stdio.h> 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"オプションは省略)
> 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
ポイント:
C言語の関数をDLLで公開し、C言語のコンソール/Windowsアプリケーションから使う簡単なサンプルで、コマンドライン上のツールを使ってDLLとそれを利用するアプリをビルドする基本的なパターンをまとめる。
※DLLは、C++やMFC, VB側からの呼び出しなど幅広く関連しています。より深くDLLを理解し、使いこなすには、ぜひMSDNを参照して下さい。
参考MSDN(Express Edition):
※またこの記事では関数のみをDLLで公開します。データを公開したりアプリケーション/DLL間で共有する場合は以下のMSDNを参照して下さい。
参考MSDN(Express Edition):
CLコマンドに"/LD"オプションを指定することで、ファイル分割無しの場合にDLLまで一気に生成するサンプル。
libdllfoobarbaz.c:
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; }
コンパイル:
> 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でエクスポート領域を確認してみる:
> 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()関数はエクスポートされていないことを確認出来る。
ポイント:
VC++のテンプレートで使われている定義については、次のファイル分割有りのパターンで示す。
libdllfoo.c:
int __declspec(dllexport) foo(int a, int b) { return a + b; }
libdllbar.c:
int __declspec(dllexport) bar(int a, int b) { return a * b; }
libdllbaz.c:
int baz(int a, int b) { return a - b; }
コンパイル:
> 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++のWin32DLLのテンプレートで使われている"/D":
/D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "(XXYY)DLL_EXPORTS" /D "_WINDLL" /D "_UNICODE" /D "UNICODE"
リンク:
> 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を使うアプリ側で、インポートライブラリを使った暗黙的なリンクを行うサンプル。
usedll_implicit.c: DLLから呼ぶ関数を "__declspec(import)" を指定して宣言する。
#include <stdio.h> 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をビルドした時に生成されたインポートライブラリを一緒に指定する。
> 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
手動でDLLをロードし、関数ポインタを取得するサンプル。
usedll_explicit.c:
#include <stdio.h> #include <windows.h> 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; }
コンパイル&実行:
> 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