home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

C言語系/memos/VC++/04, Win32のEXE,LIB,DLL開発入門(C言語)

C言語系/memos/VC++/04, Win32のEXE,LIB,DLL開発入門(C言語)

C言語系 / memos / VC++ / 04, Win32のEXE,LIB,DLL開発入門(C言語)
id: 587 所有者: msakamoto-sf    作成日: 2010-02-14 13:00:28
カテゴリ: C言語 Windows 

CLR, .NET, C++ は使わずに、C言語とCRT(Cランタイムライブラリ)とWin32APIだけを用いて、コンソール/Windows/スタティックライブラリ/DLLを、コマンドラインからコンパイルして作成する方法のまとめ。UNICODE対応は使わない。

※複数ファイルの分割コンパイル+リンクについては C言語系/memos/VC++/01, 共通系コンパイルオプション 参照。

参考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.

コンソールアプリ

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_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:

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++のテンプレートに従い、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:

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++のテンプレートに従い、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:

#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

ポイント:

  • 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:

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()関数はエクスポートされていないことを確認出来る。

ポイント:

  • "/LD"だとデバッグ情報無し。"/LDd"でデバッグ用のDLLを生成する。
  • 出力ファイル名を指定したい場合は "/Fe" オプションで指定する。

VC++のテンプレートで使われている定義については、次のファイル分割有りのパターンで示す。

DLL側のビルド例2(ファイル分割, CLコンパイル+LINKでDLL生成)

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++のテンプレートに従い、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"

リンク:

> 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)" を指定して宣言する。

#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

明示的なリンク(explicit link)を使ったアプリ側のビルド

手動で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


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-02-14 13:02:40
md5:488397907cc1d08bbf8fb8e292ca2baa
sha1:3579429c75ce9b462202b485b8028b01b035e14d
コメント
コメントを投稿するにはログインして下さい。