\n
[ トップページ ]
[ ___ _CommandPrompt ]
[ ___ _JScript ]
[ ___ _MySQL ]
[ ___ _Cygwin ]
[ ___ _Java ]
[ ___ _Emacs ]
[ ___ _Make ]
[ ___ _Perl ]
[ ___ _Python ]
[ ___ _OpenGL ]
[ ___ _C# ]
[ ___ _StyleSheet ]
[ ___ _C++ ]
[ ___ _Winsock ]
[ ___ _Thread ]
[ ___ _VisualStudio ]
[ ___ _C ]
[ ___ _Win32API ]
[ ___ _Lua ]
[ ___ _PhotoShop ]
ヘッダ検索
■ CommandLine.からの実行
■ 下準備
■ IncludePath.LibraryPath.を通す
Compiler と Linker のパスを通す
基本の IncludePath, LibraryPath を通す
VisualStudio では 環境変数 INCLUDE, LIB にあるパスを検索する
そこで profile などで設定しておく
# VisualStudio Install Directory
VS=d:/vs
INCLUDE=$VS/VC/include
LIB=$VS/VC/lib
WARNING
ENV:INCLUDE.LIBはVS2005では環境変数を設定しても正しく動作しない
// /useenv すれば OK
devenv.exe /useenv
■ Directory.構成
$VS
/VC // C++ 用 Directory
/lib
/include
/bin
/cl.exe
/link.exe
/lib.exe
/VB
/VC#
WARNING
2005, 2008 が混在した環境で, lib, exe の設定を間違えると以下のエラーがでる
必ず build 設定を合わせること
header の違いによるものと思われる
解決策として lib, exe 共に, 同一バージョンでビルドして Link すれば治る。
thiscall std::_Container_base::~_Container_base(void)" (__imp_??1_Container_base
@std@@QAE@XZ) が関数 "public: __thiscall std::_Vector_val< class IndexBuffer *,cl
ass std::allocator< class IndexBuffer *> >::~_Vector_val< class IndexBuffer *,clas
s std::allocator< class IndexBuffer *> >(void)" (??1?$_Vector_val@PAVIndexBuffer@
@V?$allocator@PAVIndexBuffer@@@std@@@std@@QAE@XZ) で参照されました。
■ チュートリアル(Tutorial)
■ とりあえずビルドして実行ファイルをつくる
簡単なコードを用意
#include< stdio.h>
int main() {
printf( "test\n" );
return 0;
}
main.cpp をコンパイルしてリンクする
出力実行ファイル名を指定する。
cl /out:test.exe main.cpp
POINT
すべてのコンパイルオプションをファイル出力しておくと便利。
cl /help > d:/cl_doc.txt
■ Compile
ファイル単位でコンパイルして、リンクをする
// /c でコンパイルのみの指定
cl /c main.cpp
// /out:FILE で出力実行ファイル名の指定をする
link /out:test.exe main.obj
■ Debug.のためのビルド設定
次の設定が必要になる。
// デバック用のオプションを指定してコンパイル
cl /MDd /Zi /Od main.cpp
// そしてリンク
link /DEBUG main.obj
WARNING
pdb ファイル名( Path )を勝手に変更しないこと
exe に pdb のパスが書かれているデバッガがシンボル情報を見つけられなくなる
"main.exe のデバッグ情報が見つからないか一致しません"
// 名前をかえてみる
mv main.pdb foo.pdb
POINT
正しくシンボルテーブルが読まれているかは [ 出力 ]ウィンドウで確認できる。
'test.exe': 'D:\test\release\test.exe' を読み込みました。シンボルが読み込まれました。
// ここは /MD にしているから
// /MTd にしても Windows System DLL の Symbol は Load されない
'test.exe': 'C:\WINDOWS\system32\ntdll.dll' を読み込みました。シンボルが読み込まれていません。
'test.exe': 'C:\WINDOWS\system32\kernel32.dll' を読み込みました。シンボルが読み込まれていません。
'test.exe': 'C:\WINDOWS\system32\msvcrt.dll' を読み込みました。シンボルが読み込まれていません。
プラグインを開発する時は, 実行ファイル本体のシンボルテーブルは存在しない。
その場合はデバッグ開始時に次のような表記がでる。
app.exe のデバッグ情報がみつからないか, または一致しません。
さらにデバッガは exe が参照する DLL を順番にロードする。
ここでデバッグ対象の DLL のシンボルが読み込まれているか確認する。
app.exe': 'd:/dll/test.dll' を読み込みました。シンボルが読み込まれました。
■ /Zi /ZI /Z7
DESC
生成する Debug.情報 の種類を指定する
POINT
Debug.情報とは
変数や関数の名前, 型, 行番号のリストのこと
サードパーティのライブラリを配布する場合は
.pdb ファイルを持たない利点あり
ただし
プリコンパイル済みヘッダーの .obj ファイルは
リンク フェーズおよびデバッグで必要です
.pch オブジェクト ファイルに型情報だけがある (コードはない) 場合は
/Yl (デバッグ ライブラリの PCH 参照の挿入) も指定してコンパイルする必要あり
POINT
次の指定だけでも Debug.情報を生成できる
POINT
VisualStudio 上の設定は次のようにする
Compile Option
全般 > Debug.情報の形式
> 無効 // BP に Hit しない
> ProgramDatabase(/Zi) // BP に Hit
> エディット コンティニュ用プログラム データベース
Linker
Debug > Debug.情報を生成
> いいえ // pdb ファイルが生成されない && BP に Hit しない
> はい ( /DEBUG ) // pdb ファイルが生成 && BP に Hit
■ SymbolTableを作成する
// Debug をするには SymbolTableを作成する
// そのための option として /Zi を指定
cl /c /Zi main.cpp
// vc90.pdb ( Program Data Base File が作成される )
// obj ファイルに組み込むには /Z7 を指定する
// main.obj のサイズが大きくなることがわかる
cl /c /Z7 main.cpp
WARNING
Debugger が外部の SymbolTable を見つけるためには同一箇所に置く必要がある
■ DLL.を作成する
int add( int a, int b ) {
return a + b;
}
// コンパイル
cl /c dll.cpp
// リンカーに DLL を作成するように指示する
link /DLL /EXPORT:add dll.obj
■ DLL.の.Debug
POINT
DLL の Debug を開始できる Project は 次の 2 つ
DLL をコールする実行可能ファイルプロジェクト
DLL を作成するプロジェクト
POINT
DLL を作成したプロジェクトからデバッグを開始するには
DLL のデバッグに使用する実行可能ファイルを指定する
構成 > Debug > Command
d:/myapp/myapp.exe
■ Debug.に役立つ関数
■ DebugBreak(__debugbreak)
SYNTAX
void DebugBreak();
DESC
現在のプロセスでブレークポイント例外を発生させる
デバッガに信号を送り何らかの処理を強制的に実行できる
プロセスがデバッグ中でないときは
標準例外ハンドラの検索ロジックが使われます
ほとんどの場合
未処理のブレークポイント命令のために呼び出し側プロセスは終了する
■ __debugbreak
SYNTAX
__debugbreak();
DESC
コンパイラの組み込み関数
DebugBreak __debugbreak を呼び出した場合の動作は
その位置にブレークポイントを設定することと同じ。
■ debug.有無の開始
DESC
c-f5 : debug なし 開始
f5 : debug あり
-> 違い
break point の判定
POINT
c-f5 で実行した場合, terminal に実行結果が停止して表示される
WARNING
assert がスキップされるわけではない( assert は _DEBUG にたしいて有効 )
■ debug.開始directoryの設定
DESC
SE > RMB > 構成プロパティ > debug > 作業 dir
POINT
SE > RMB > スタートアップ プロジェクトに設定
--------------- // ( 以下従来の方法 )
-> SE > Debug > 新しいインスタンスを開始 ( これで修正可能な場合もある )
path の問題かどうかは, shell から直接実行することで確認できる.
■ 条件付きbreakpoint
DESC
条件:
条件式が真の場合に停止 ->
2. 値が変更:
i :
*p : p の指す値
3. hitcount :
for loop の際に使用
i >= 2
i*i >= 10
strcmp( data[i], "aaa" ) == 0 ERR
pow( i, 2 ) >= 10 ERR
■ 混合モード
DESC
ソースコードとコンパイラによって生成されたアセンブリコードを
交互に混ぜて表示するモード。
POINT
クラッシュしたアドレスがわかっている場合にデバッグで役にたつ。
Debug > Window > 逆アセンブル ( Alt - 8 )
現在のコードに移動する。
ステップ実行( F10 )をした場合は, アセンブラコードの単位で一行ずつ実行される。
// ソースコードと
7: main()
// アセンブリコードが交互に表示される
0040B7B0 push ebp
POINT
アプリケーションがクラッシュした場合に表示されるダイアログを見れば, どこで落ちたかわかる。
障害モジュールの名前: test.exe
■ スタティックライブラリをつくる(StaticLibrary)
DESC
スタティックライブラリとは、コンパイル済みのオブジェクト( *.obj )をアーカイブしたもの。
LINK はしないので, Linker の設定は不要
よく利用するモジュールは StaticLibrary として用意しておくと
実行ファイルをつくるときに、コンパイルせずにリンクだけで完了するので便利。
math のモジュールをつくる場合
math
vector.cpp
matrix.cpp
$(VC)/VC/bin/lib/Lib.exe を利用してアーカイブする。
// コンパイルして
cl /c vector.cpp matrix.cpp
// アーカイブする
lib vector.obj matrix.obj
作成した StaticLibrary を利用する場合はコンパイル済みなので link するだけで完了する。
link main.obj math.lib kernel32.lib
WARNING
Library 内の関数のみが lib に含まれる
PlatformSDK などを利用するなら, exe 側で LINK すること
POINT
C/C++ > コード生成 > ランタイムライブラリ の修正を忘れないこと ( 大量のエラーがでる )
POINT
Application 側で Debug するには pdb というファイルが必要
これを lib と同じ場所においてやる必要がある
POINT
Application 側は次のような設定をする
C/C++ > コード生成 > ランタイムライブラリ
DLL なし 版にきりかえる
WARNING
DLL, exe を作成するときは利用してはダメ
POINT
LibraryManager のこと
■ Debug.する
■ Debug関連の設定
デバッグするには問題箇所の内容を見るため、
ソースコードにブレークポイントをしかける。
Break Point の設定 ソースコードの左側を設定
開始をするには、 F5 を押す。
ブレークポイントの箇所でプログラムの実行が停止するので
F10, F11 で一歩ずつプログラムの動作を調べる。
一時的に無効にすることもできる。
property : exe のカレントパスと引数を設定
■ マクロを使う
■ ビルドする環境を変更する
環境ごとに処理を変えたい場合は、マクロ変数の値におうじて処理をかえる。
#if BUILD_TARGET == BUILD_TARGET_WIN32
typedef __uint32 uint32_t
#else
#include< stdint.h>
#endif
cl /D"BUILD_TARGET" main.cpp
#ifdef BUILD_TARGET
typedef int uint32_t;
#else
# include< stdint.h>
#endif
■ Comment.別エラー対処
■ cl.exe.の起動中に.Error.が発生しました
原因
何かの理由で VisualStudio の PATH がとおっていない
VC/bin/cl.exe
■ オーバーロードされた関数functionのC.リンケージの2回以上の宣言は許されません
オーバーロードされた関数'function'のC.リンケージの2回以上の宣言は許されません
DESC
extern "C" void foo();
extern "C" void foo( int );
overload func を非修飾にすることはできないということ
■ PROJ0019
ProjectBuildError
たとえば
Build.前のイベントにエラーがある
呼び出し先のどこかでエラーを起こしている
■ LINK_ERROR
■ LNK2005
MESSAGE
既に MSVCRT.lib(MSVCR80.dll) で定義されています
SYNTAX
Symbol( 本名 )は Object で定義されています。
DESC
リンク時に複数のモジュールで同名のシンボルがある時に発生する。
main.cpp に void func() がある
void func(){
printf( "main func()" );
}
int main()
{
// func() をコールする。
func();
return 0;
}
module.cpp にも同名の void func() がある。
void func(){
printf( "module func()" );
}
リンカーがどちらの func() を利用すればいいか分らないのでエラーとなる。
名前が重複しないようにすれば解決する。
リンク時に以下の理由で発生するエラー。
// 自作のModule
-------------
A.obj
MSVCRT.lib // Complile 時の指定で埋め込まれている ( 動的 LINK する予定 )
-------------
// 他からひろってきた Module
-------------
module.obj
MSVCRT.lib // 別の Object は静的 LINK する予定
-------------
Linkerからすると、どちらのlibを利用すべきか分らないことになる。
解決方法
1. 自分のつくった Obj の CRT のリンク先を よその Module の CRT のリンク先にあわせる
2. 自分のつくった Obj の CRT のリンク先と同じものを指す Module を LINK する
■ LNK4098
DESC
リンクする CRT ライブラリの指定がモジュールごとに異なっている。
例えばデバッグ版とリリース版が混ざっていると発生する。
// リリース版を指定
cl /c /MD main.cpp
// こちらは デバッグ版を指定
cl /c /MDd module.cpp
解決するには、すべてのコンパイル単位で CRT の指定をそろえる。
cl /c /MD main.cpp
cl /c /MD module.cpp
cl /c /MDd main.cpp
cl /c /MDd module.cpp
cl /c /MT main.cpp
cl /c /MT module.cpp
実際におきるケースは既にあるコンパイル済みの外部ライブラリをリンクするときに発生する。
POINT
Compile した obj ( Library )を link するときに CRT の指定が統一されてないとおきる
外の Library だけではなく, わざと cpp 単位で設定をかえるとおきる
LINK : warning LNK4098: defaultlib 'LIBCMT.lib' は他のライブラリの使用と競合しています
/NODEFAULTLIB:library を使用してください
C, C++でよく使う printf() はコンパイルしてライブラリという形で用意されてる
必須のライブラリをランタイムライブラリという
POINT
Runtime Library とは必須の Library ( よく使う Library ) のこと
デバッグ用とかマルチスレッド用、MFCを使う場合、DLLを使う場合などでライブラリを変える必要がある
どのライブラリとリンクするかは
VisualStudioでプロジェクトがやってくれるので大抵は気にする必要はない
何か別のライブラリを使いたいという場合に面倒になる
そのライブラリがリリース用の libcmt.lib とリンクするように指示が組み込まれていて
でもデバッグしたいから自分はlibcmtd.libとリンクしたい場合に問題になる
そのライブラリのlibcmtd.libとリンクするバージョンを用意すれば解決する
そこで libcmt.lib とリンクさせないようにする
VCで他の Lib( obj ) をリンクするメッセージが出る
LINK : warning LNK4098: defaultlib "msvcrtd.lib" は他のライブラリの使用と競合しています; /NODEFAULTLIB:library を使用してください
LINK : warning LNK4098: defaultlib "nafxcwd.lib" は他のライブラリの使用と競合しています; /NODEFAULTLIB:library を使用してください
LINK : warning LNK4098: defaultlib "libcmtd.lib" は他のライブラリの使用と競合しています; /NODEFAULTLIB:library を使用してください
よく、「スタティックライブラリでMFCを使用」と出ていたので、「共有DLLでMFCを使用」に変更していたが
ランタイムライブラリが
VisualStdio で
シングルスレッド
マルチスレッド(DLL)
マルチスレッド
で3種類。デバッグ、リリース版で計6種類用意
、
MSVCRT.lib マルチスレッド(DLL) Release /MD(コンパイルオプション)
MSVCRTd.lib マルチスレッド(DLL) Debug /MDd
LIBCMT.lib マルチスレッド Release /MT
LIBCMTd.lib マルチスレッド Debug /MTd
------------------------------------------- VS2005 からはない
LIBC.lib シングルスレッド Release /ML
LIBCd.lib シングルスレッド Debug /MLd
コンパイラーオプションでの設定は
「プロジェクトの設定」 > 「C/C++」 > カテゴリ「コード生成」
POINT
ライブラリで作成したときにリンクしたランタイムライブラリと
現在のアプリで使用している RunTimeLibrary で異なっているためにエラーが出ている
ライブラリのソースコードがあれば
コンパイルオプションをアプリと同じオプションに変え作成しなおせば直せる
ソースコードがなければ、ライブラリのオプションにアプリを合わせないとだめ
アプリが 「マルチスレッド」
ライブラリが「マルチスレッド(DLL)」の場合
警告は出るが、使える。
POINT
Library 側ですべての 構成を用意すること
\md\test.lib
\mdd\test.lib
\mt\test.lib
\mtd\test.lib
使う側では
test.lib
LibraryPath で読込先を変更する
// Release 版
/mt
// Debug 版
/mtd
LINK : warning LNK4098:
defaultlib ' MSVCRT ' は他のライブラリの使用と競合しています。/NODEFAULTLIB:library を使用してください。
/data/tool/vc2005/VC/bin/mt.exe /nologo /manifest debug/win32beep.exe.manifest -outputresource:debug/win32beep.exe
■ Pragma.一覧
■ Linker.に.lib.を指定する
DESC
// Linker に対する 指示( pragma )
#pragma comment(lib, "winmm.lib" )
// src( .cpp )にしている
#pragma comment(lib,"wsock32.lib" )
■ GUI
■ 検索
■ Symbolの検索(c-s-F)
編集 > 検索と置換 > folda を検索して置換. ( Symbol を列挙可能 )
DESC
現在の project のみを調べる
調べたい project を SE から選択
c-f > 現在のプロジェクトにチェック
VC CRT リファレンス
F1 > 検索する文字列[ alloca ], フィルタ条件[ Visual C++ ドキュメント ]
■ Debugテクニック
■ BreakPoint
■ CallStackWindow
DESC
関数呼び出し stack(frame) が表示される
debug > window > local
POINT
call stack == 現在 呼出し中の stack
呼び出し処理を返却 adr を格納するにも使用
thread には 1 callstack が存在
stackframe == sub rougin 毎に stack に格納する情報
frame の領域は stack ptr( register の一種 ) を使用して access する
DESC
break point までの最短 呼び出し 履歴
関数呼び出し履歴
debug > window > 呼び出し履歴
stack として表現
WARNING
すべての関数の call 履歴ではない
startup() -> winmain() -> App::run()
-> App::end() -> App::quit()
POINT
呼び出し元のDLLもリストされる。
ソースがある場合にのみ、ブレークポイントを設定できる。
■ _malloc_dbg
// Memory を確保する部分を決める
_malloc_dbg( NORMAL_BLOCK );
■ Process.に.Attach
WARNING
Express Edition は未対応
■ その他
■ ビルド前のイベント
DESC
property > ビルドイベント 各projビルド時に起動する.exe の設定
EX
test.bat( sln 相対 )
■ .proj(file)をひとつのslnにimportする
DESC
SE > RMB > 追加 > ( 既存 | 新規 )Project
■ Class.図
c-m-c -> Diagrasm
■ 環境設定(Customize)
■ MACRO.をつくる
Tool > Macro > MacroExplorer
■ ErrorCode
■ C1083
MESSAGE
file `` が開けない
DESC
Link, Compile , PreCompileHeader などの file が見つからない
SOL
PATH を変更する
■ C2017
Define の \ の跡に SPACE があった
要は Escape は "" ないにないとだめ
■ C2589
class Foo{};
Foo::int a;
..\console.cpp(70) : error C2589: 'int' : スコープ解決演算子 (::) の右側にあるトークンは使えません
DESC
[ :: ] の右側には Class, Struct, Union 名のどれかを指定すること。
または 任意のメンバだけですよ !
クラス、構造体、または共用体の名前がスコープ解決演算子 (::) の左側にあるとき
右側のトークンにはクラス、構造体、あるいは共用体のメンバを指定する必要があります
それ以外の場合、右側には任意のグローバル識別子を指定できる
スコープ解決演算子はオーバーロードできない
次の例では C2589 エラーが生成されます
■ C2220
MESSAGE
警告はすべてエラーとして処理されますエラーが発生したため、オブジェクトまたは実行可能ファイルは生成されませんでした
DESC
コンパイルオプション "/WX" を指定したため Warning をエラー扱いにしてコンパイルが中断された。
Warning をエラー扱いにしたくないなら /WX を指定しない、または すべての Warning をなくす。
// error C2220: 警告をエラーとして扱いました'object' ファイルは生成されません
// warning C4101: 'i' : ローカル変数は 1 度も使われていません
int main() {
int i;
return 0;
}
■ C2632
MESSAGE
'type1' の後に 'type2' を記述するのは正しくありません
DESC
int, float
// Type Identifier が連続するといわれる
int float i; // C2632
// bool が定義すみの環境では C2632
< typedef unsigned char bool;
■ C2653
..\foo.cpp(56) : error C2653: 'std' : 識別子がクラス名でも名前空間名でもありません
[ std ] という Class, NameSpace は知りません と Compiler がいっている
■ C4100
参照されない 仮引数の警告
WarningLevel4 の対策
解決方法
消す || 参照する. (void)arg; || arg; // (void) 型に cast
MESSAGE
';' : 制御が空の文が見つかりました意図した記述でしょうか
DESC
if (1); 制御文の後に ; があるときにでる
EX
int main() {
int i = 0;
if (i); // C4390
i++;
}
POINT
MACRO を展開したときにこうなる可能性がある
■ C4390
MESSAGE
';' : 制御が空の文が見つかりました意図した記述でしょうか
DESC
if (1); 制御文の後に ; があるときにでる
EX
int main() {
int i = 0;
if (i); // C4390
i++;
}
POINT
MACRO を展開したときにこうなる可能性がある
■ C4267
DESC
64bit 環境の size_t 型から, 小さいサイズの型に変換する際に発生する。
size_t は環境ごとに次のように定義されている
// 32bit
typedef unsigned int size_t;
// 64bit
typedef __int64 size_t
■ C4244
MESSAGE
'引数' : 'type1' から 'type2' への変換です。データが失われる可能性があります。
DESC
浮動小数点型が整数型に変換されました。データが失われた可能性があります。
互換性のある型を使用するようにプログラムを変更するか
別の論理をコードに追加するかして、変換される値の範囲が使用する型と常に互換性があるようにする
int f(int x){ return 0; }
int main() {
double x = 10.1;
int i = 10;
return (f(x)); // C4244
// try the following line instead
// return (f(i));
// C4244
float f = 10;
}
範囲内にあるときも同様になる
float f = 1000;
WARNING
// 範囲外にあれば int 型のデータは float 型へ変換した際に欠落する
float f = 1024 * 1024 + 1;
float i = f;
引数' : 'unsigned int' から 'float' への変換です。データが失われる可能性があります。
■ C4018
MESSAGE
'expression' : signed と unsigned の数値を比較しようとしました。
C4018: '< ' : signed と unsigned の数値を比較しようとしました。
signed 型の数値と unsigned 型の数値を比較するには、コンパイラで signed 型の値を unsigned 型に変換する必要があります。
signed 型と unsigned 型をテストするときに
どちらか一方の型をキャストすると解決することがあります。
POINT
マイナスの値と比較するとエラーが発生する
逆に正の値しか扱わない場合はエラーにならない
for( int i=0; i< listLog.size(); i++ ){
■ C4800
MESSAGE
'int' : ブール値を 'true' または 'false' に強制的に設定します (警告の処理)
'type' : ブール値を 'true' または 'false' に強制的に設定します (警告の処理)。
非 bool 値を bool 型に代入するか強制的に変換するとこの警告が生成されます。
値 true または false しか取らない int 変数を bool 変数に代入しており、
bool 型として再宣言できる場合が含まれます。
bool 型を使用するように式を書き直すことができない場合は、式に "!=0" を追加して bool 型にできます。
式をただ bool 型にキャストしても、この警告が生成されます (設計仕様)。
// compile with: /W3
int i = 0;
// try..
// bool i = 0;
bool j = i; // C4800
j++;
■ C4291
MESSAGE
'void *Test::operator new(size_t,unsigned int) throw()' : 初期化コードが例外をスローすると、'new' 演算子を使用しているとメモリを解放しません。
DESC
宣言' : 初期化コードが例外をスローすると、'new' 演算子を使用しているとメモリを解放しません。
仮引数付きの new が使用されていますが
該当する仮引数付きの delete がありません。
new 演算子でオブジェクトにメモリを割り当てると
そのオブジェクトのコンストラクタが呼び出されます。
コンストラクタが例外をスローした場合は、オブジェクトに割り当てられていたメモリを解放する必要があります。
new 演算子と一致する operator delete 関数がない場合、メモリの解放はされない。
余分な引数を付けずに new 演算子を使用し、/GX、/EHs、または /EHa の各オプションでコンパイルして例外処理を有効にすると
コンストラクタが例外をスローしたときに delete 演算子を呼び出すコードが生成されます。
仮引数付きの new 演算子
つまり割り当てサイズ以外の引数を伴う形式の new 演算子を使用すると、
コンストラクタが例外をスローしたときにも、
delete 演算子を呼び出すコードは生成されます。
これは、メモリを割り当てた仮引数付きの new 演算子と一致する仮引数付きの delete 演算子がある場合だけです。
仮引数付きの new 演算子と一致する仮引数付きの delete 演算子が定義されていないため、警告 C4291 が生成されます。
この問題を解決するには、main の前に次のコードを挿入します。
オーバーロードされた operator delete 関数のパラメータは、最初のパラメータを除き、
すべてオーバーロードされた new 演算子と一致することに注意してください。
// throw を宣言すると 例外が投げられることになる
void* operator new(size_t t, unsigned int id ) throw();
// 宣言をしないと エラーにならない
void* operator new(size_t t, unsigned int id ) ;
■ C4305
MESSAGE
'identifier' : truncation from 'type1' to 'type2'
識別子が小さい型に変換されるため、情報が失われます。
// float 型に double 型の値をいれる発生する
v.set( 0, 0.142857, 0.342857 );
// 型に合った値をいれればなおる
warning C4305: '引数' : 'double' から 'float' へ切り詰めます。
■ C4312
MESSAGE
'type1' からより大きいサイズの 'type2' へ変換します。
DESC
この警告は 64 ビットの移植性の問題を検出します。
64 ビットの型に対して 32 ビットの値を割り当てようとしました。
32 ビット int または 32 ビット long を 64 ビットポインタにキャストしています。
符号の拡張が発生する場合、
一部の環境では
これは安全でない変換になります。
int 型よりもサイズが大きいポインタ型に負数が割り当てられると
符号の拡張 が発生するため
ポインタ型の値が int 型の値と異なるメモリ アドレスを参照するようになります。
この警告は
/Wp64 が使用されている場合にのみ出される。
// C4312.cpp
// compile with: /W1 /Wp64 /LD
void* f(int i) {
return (void*)i; // C4312
}
// OK
void* f2(__int64 i) {
return (void*)i;
}
■ C4313
MESSAGE
'function' : 書式指定文字列内の 'format specifier' が 'type' 型の引数 %d と競合しています
POINT
printf() の format 指定子 が, 引数の型と矛盾したときにおきる。
{
void *p = malloc( 1024 );
// ERROR
// 64bit pointer
printf( "%d" , p );
// OK
printf( "%p" , p );
printf( "%Id" , p );
}
64 ビット整数を保持できる %d 書式指定子に 32 ビット ポインタを渡している。
この警告が有効なのは /Wp64 オプションを使用している場合だけ
■ C4313
■ オーバーロードのどれも、すべての引数の型を変換できませんでした
DESC
問題箇所の関数がどれもオーバーロードする関数にならないということ。
引数が間違っているかチェックすること。
■ C4996
MESSAGE
DESC
セキュリティ的に推奨されない関数が利用されている。
FILE *fp = fopen( "test.txt" , "r" );
warning を消すには _CRT_SECURE_NO_WARNINGS を定義するか、
warning プラグマ を利用する
#pragma warning(disable : 4996)
cl /W4 /D"_CRT_SECURE_NO_WARNINGS" main.cpp
■ C4530
MESSAGE
FILE(LINE) : warning C4530:
C++ 例外処理を使っていますが、アンワインドセマンティクスは有効にはなりません。
/EHsc を指定してください。
原因
標準 C++ ライブラリを使用するプログラムは、
C++ の例外処理を有効にしてコンパイルする必要がある。
( 理由は ライブラリが例外処理をつかっているから )
解決方法
/EHsc オプションを有効にする。
[C/C++] > [コード生成] > [C++ の例外を有効にする]
cl /EHsc
// 標準ライブラリを利用する
#include< string>
std::string s( "test" );
標準ライブラリを利用する場合は、例外処理を有効にしてコンパイル(コード生成)をする。
標準ライブラリを利用しないでも C++例外処理を使うと( try { 文の箇所で ) warning が発生する。
try {
throw "例外が発生しました" ;
}
catch( const char* str ){
printf( "例外をキャッチしました\n" );
}
デストラクタがすべてのオブジェクトに対して呼び出されない場合があります。
ただし初期化されていない関数ポインターを通じて関数呼び出しを実行しようとして
その関数として呼び出す前に作成した
パラメーターのオブジェクト
たとえば構造化例外が発生する場合これらのオブジェクトに
アンワインドするスタック中に呼び出されたデストラクターはありません。
REFERENCE Exception
■ リンカーオプション(LinkerOption)
■ Runtime
■ /implib
SYNTAX
/IMPLIB:filename
DESC
Link で生成される ImportLibrary 名を filename に上書きする
Default は basename
test.dll ならば test.lib
この Option の指定は必須ではない
UI
Linker > 詳細 > ImportLibrary
POINT
この Option は必須ではない
■ /LD
WARNING
この Option 自体は lib.exe のものではない
// cl の option のひとつ
DESC
Link 時の指定
DLL を作成する
これをつけると _main() がないと怒られない
// dll を build する
cl /LD /EHsc dll.cpp
// 2 つの Source
cl /LD /EHsc dllA.cpp dllB.cpp
// DLL 名を指定する
// test.dll, test.lib が作成される
cl /LD /EHsc dllA.cpp dllB.cpp /link /out:test.dll /implib:test.lib
// ImportLibrary を指定して Build
cl /EHsc call.cpp test.lib
// 実行
./call.exe
// test.dll の名前を変更すると ERROR
// test.dll が見つからなかったため このアプリケーションは実行できませんでした
mv test.dll test.dll_bk
__declspec( dllimport ) void dllfuncD();
int main()
{
dllfuncD();
return 0;
}
■ Debug
■ /DEBUG
DESC
.exe ファイルまたは DLL のデバッグ情報を作成するために指定する
デバッグ情報は、プログラム データベース (PDBファイル) に書きこまれる
デバッグ用に生成された .exe や DLL には
対応する PDB ファイルの名前とパスが書かれている
シンボルが読まれない時は以下のコマンドで調べること
strings main.exe | grep "pdb"
デバッガーでプログラムをデバッグするときにこの名前が読まれ
その PDB ファイルが使われる
'vc.exe': 'D:\test\vc\debug\vc.exe' を読み込みました。シンボルが読み込まれました。
プログラム データベースの名前として
ベース名 .pdb が使用され
PDB ファイルのパスは .exe, .DLL ファイルに書かれる
オーバーライドするには /PDB:filename で指定
link /DEBUG /PDB:foo.pdb main.obj
/Zd (行番号のみ) または /Z7 (C7 互換) を指定すると
.obj ファイルにデバッグ情報が保存される
/Zi (プログラム データベースを使用) コンパイラ オプションを指定すると
.obj ファイルのデバッグ情報が PDB ファイルに保存されます
リンカーはオプジェクトの PDB ファイルを探すときに
まず .obj ファイルに書き込まれた絶対パスを検索し
そこで見つからない場合は .obj ファイルの置かれているディレクトリを検索します
オブジェクトの PDB ファイルの名前やディレクトリをリンカーに指定できない
■ /PDB
SYNTAX
/PDB:filename
DESC
リンカーが作成するプログラムデータベース (PDB) の名前を指定する
( Default : プログラムベース名.pdb )
/DEBUG (デバッグ情報の生成) を指定すると
デバッグ情報の入ったプログラム データベース (PDB) が作成される
POINT
pdb file の生成は /DEBUG で有効にする
/DEBUG を指定しないと /PDB オプションは無視
PDB ファイルは 最大 2 GB
Debug 中に Build すると pdb File を利用されていることがわかる
「リンカー入力としての .pdb ファイル」
■ DLL
■ /DLL
DESC
DLL をビルドする
この Option をつけて Compile すれば DLL が作成される( はず ) -> 2010.07.22 -> ちがった
Link 時に指定する Option のため ,
Compile 時は特に関係ないはず
DLL の作成方法
POINT
VC の version によっては wizard で DLL を選択できない
でも Console Application を指定して 後で変更できる
( Option の設定しだいということ )
■ /export
DESC
symbol を export する
プログラムから関数をエクスポートして
ほかのプログラムがその関数を呼び出せるようにする
// こうするかわりに
__declspec( DLL_EXPORT ) void func;
// こうする
// [追加のオプション] ボックスにオプションを入力
/export:func
■ その他
■ MACHINE
DESC
対象となるアーキテクチャを指定する
/MACHINE:x86
/MACHINE:x64
/MACHINE:I386
DLL は同一のタイプを利用しないと
Module の対象 Computer と異なるといわれる
■ Entry
DESC
EXE DLL 開始アドレス(名前)を指定する。
エントリポイントとはプログラムをメモリにロードした際に
実行をはじめる開始アドレスのこと。
Linker > 詳細 > EntryPoint
link /Entry:main main.obj
POINT
実はユーザーが書いたコードよりも前に、CRT( printf など ) の初期化、例外ハンドラの設定などが実行される。
これが _mainCRTStartup(), _WinMainCRTStartup() という関数で、C のランタイムライブラリ内にある。
次の流れになる。
main() は _mainCRTStartup() からコールされる。
// CRTの初期化をして ユーザプログラムを開始
OS ---> _mainCRTStartup() ---> main()
WARNING
エントリポイントはリンカーにまかせるべき。
そうしないと C ランタイムライブラリが正確に初期化され、
静的オブジェクト用の C++ のコンストラクタが実行されない。
#include< stdio.h>
int main()
{
printf("test\n" );
return 0;
}
CRT を利用しているが, main() を開始アドレスにする。
cl /c main.cpp
link /entry:main main.obj
実行すると次のエラーがでる。
runtime error R6030
- CRT not initialized
デフォルトのエントリポイントは、アプリケーションのタイプ( SUBSYSTEM )によって決まる。
SUBSYSTEM:CONSOLE ならば mainCRTStartup
SUBSYSTEM:WINDOWS ならば WinMainCRTStartup
■ SUBSYSTEM
DESC
オペレーティングシステムに .exe ファイルの実行方法を指定する。
サブシステムの選択によって
リンカが選択するエントリポイント関数が決まる。
サブシステムの指定は どんな環境用のコードを出力するかを決定するために指定する。
POINT
main() または WInMain() が定義されていると SUBSYSTEM オプションのデフォルト値が決まる。
// main() がコード内で定義されていると、サブシステムとして暗黙に CONSOLE を選択することになる。
int main() {
return 0;
}
明示する場合はリンカーオプションで指定する。
WINDOWS
アプリケーションにはコンソールは不要です。
このシステムにはユーザーとの対話用のウィンドウが作成されるからです。
WinMain() または wWinMain() が定義されていると、WINDOWS がデフォルト設定になる。
CONSOLE
Win32 文字モードアプリケーションに対して使用する。
コンソール アプリケーションには、オペレーティングシステムからコンソールが与えられる。
main() または wmain() が定義されていると、CONSOLE がデフォルト設定になる。
POINT
dumpbin /HEADERS でエントリポイントの開始アドレスがあることが確認できる。
undname.exe で C++ 修飾子前の名前を取得できる。
undname ?Foo@@YGHPAUHINSTANCE__@@0PADH@Z
■ Runtime
■ MANIFEST
WARNING
crt.dll の場所を明示することが目的
mt.exe で埋め込まないと exe をビルド後の場所から移動すると実行できない
( たぶん manifest ファイルも配布すれば 埋め込むのも必須ではないと思う )
POINT
VisualStudio では Default で manifest を実行ファイルに埋める
GenerateManifest="true"
Linker > Manifest > Manifest の生成
// いらない
link /MANIFEST:NO main.obj
// つくる ( Default )
link /MANIFEST main.obj
// 指定しない場合でも作成される
link main.obj
WARNING
/MANIFEST:NO にしても 自動で exe には組み込まれない
mt.exe を利用して埋め込む。
2 つの方法でアプリケーション || ライブラリに埋める
インクリメンタル ビルドを実行しない場合は
ビルド後のステップでマニフェストを直接埋める
// Exe の場合
mt.exe /manifest test.exe.manifest -outputresource:test.exe
// DLL の場合
mt.exe /manifest test.dll.manifest -outputresource:test.dll
インクリメンタル ビルドを実行する場合、上記のようにリソースを直接編集すると
インクリメンタル ビルドが無効になり
フル リビルドが実行されるため
異なるアプローチがいる
バイナリをリンクして
app.exe.manifest ファイルを生成
マニフェストをリソースファイルに変換
(インクリメント方式で) 再リンクして、マニフェスト リソースをバイナリに埋める
■ NODEFAULTLIB
LINK が参照を解決するときに検索するライブラリを引数 library で指定する
コマンド ラインで指定したライブラリが検索される
2. /DEFAULTLIB オプションで指定したライブラリが検索される
3. obj ファイル内で指定した既定のライブラリが検索される
/NODEFAULTLIB (すべての既定のライブラリを無視) オプションは、
/DEFAULTLIB:library の指定よりも優先されます
/NODEFAULTLIB:library (無視するライブラリ) オプションと /DEFAULTLIB:library
オプションで同じライブラリを指定すると、前者の指定が優先されるためそのライブラリは検索されません
POINT
自分でコンパイルをする必要があるライブラリを使用する時は
アプリケーションのランタイムライブラリの指定を、 ライブラリを生成した際の指定と同じにしておく必要がある。
異なる設定をしている時は、warning または、シンボルの二重定義でリンクできないなどの症状が出る。
Linker > Input > すべての既定ライブラリ無視 : YES
RunTimeLibrary の指定が必要なとき
MultiThread Application で SingleThread の指定が必要な場合
ライブラリとそれを利用するアプリケーションで、異なった設定を行った場合
/MD で作成されているライブラリを、/MT のアプリケーションで 利用しようとすると LIBCMT.lib での二重定義でエラーとなる
error LNK2005: _printf は既に LIBCMT.lib(printf.obj) で定義されています
/MT で作成されているライブラリを、/MD のアプリケーションで 利用しようとすると warning が出るが動作はする
LINK : warning LNK4098: defaultlib 'LIBCMT' は他のライブラリの使用と競合しています。/NODEFAULTLIB:library を使用してください。
debugで作成されているライブラリを releaseで使用した場合ライブラリの競合が報告される ( 逆も同じ )
// DLL 版でも同じ
test.lib ( /MTd )
app.exe ( /MT )
test.lib ( /MT )
app.exe ( /MTd )
■ 静的リンク
POINT
/MT, /MD とは C の Runtime == C標準関数 の Runtime の
LINK をどうするのか って聞いている
静的リンクとは
アプリケーションプログラムをリンクした時に
必要なライブラリをプログラムに含める方式
リンク時にオブジェクトファイルを汎用ライブラリと共につなぎ合わせ
実行可能形式のバイナリを作成する
利点
必要なAPIやライブラリのバージョン間の互換性を気にしなくてもよい
欠点
実行可能形式のプログラムサイズが大きくなる
共有ライブラリをバージョンアップしたときにプログラムを再リンクする必要がある
■ 動的リンク
プログラムを実行する時に初めて
共有ライブラリあるいはダイナミックリンクライブラリ(DLL)と結合される方式を動的リンクという
実行時にプログラムの結合を行う方式
プログラム作成は
一般に大規模なプログラムをモジュールに分割して
コンパイル後に
オブジェクトファイルを汎用ライブラリと共につなぎ合わせて実行可能形式のバイナリを作成する
これを静的リンクという
それとは異なり
プログラムを実行する時に初めて他のモジュールやライブラリと結合される方式を動的リンクと呼ぶ
この動的リンクを使ったライブラリを
共有ライブラリあるいはダイナミックリンクライブラリ(DLL)という
POINT
DLL は ライブラリのひとつ
利点
実行可能形式のプログラムサイズを小さくできること
共有ライブラリをバージョンアップしたときにプログラムを再コンパイルする必要がない
欠点 ( DLL 地獄 )
暗黙的に特定のバージョンの共有ライブラリの内部処理や仕様に依存していたプログラムが
ライブラリのバージョンアップによって動作しなくなること
バージョンアップした共有ライブラリに不良が作り込まれているとコンピュータ全体に影響が及ぶこと
バージョンアップによる影響範囲を事前に特定できないこと
複数のバージョンのライブラリがシステム内に存在するときの動作が特定できないこと等がある
■ コンパイラオプション(CompilerOption)
一覧を見るには cl.exe のヘルプを実行する。
■ Code.生成
POINT
リンカー渡されるすべてのモジュールは
同じランタイムライブラリ コンパイルオプション (/MD、/MT、/LD) を指定してコンパイルされている必要がある。
■ MT
マルチスレッド対応のスタティック バージョンのランタイム ライブラリが使用される。
POINT
コンパイラにライブラリ名 LIBCMT.lib を .obj ファイルに挿入させるため、
リンカーは LIBCMT.lib を使って外部シンボルを解決できる。
テキストエディタでコンパイル済みのオブジェクトを見ると、以下の記述があることがわかる。
■ MD
アプリケーションで
マルチスレッド対応バージョンおよび DLL 対応バージョンのランタイムライブラリが使用されます。
_MT および _DLL を定義し、
コンパイラにライブラリ名 MSVCRT.lib を .obj ファイルに挿入させます。
このオプションを使用してコンパイルされたアプリケーションは、
MSVCRT.lib に静的にリンクされます。
このライブラリは、リンカーが外部参照を解決できるようにするコードレイヤーを提供しています。
実際のコードは MSVCR100.DLL, に入っているので、
MSVCRT.lib とリンクされるアプリケーションの実行時には、
MSVCR80.DLL にアクセスできるようにしておく。
Cランタイムライブラリを DLL として利用する場合は /MD オプションでコンパイルする。
WARNING
/MD オプションでコンパイルしたオブジェクトをリンクすると manifest ファイルが生成される。
この manifest ファイルが DLL 本体の場所を示すため、実行ファイル(exe) と共に配布する必要がある。
main.cpp
main.obj
main.exe
main.exe.manifest
試しに manifest ファイルを削除してみる。
もし実行ファイルのみの環境で実行するとエラーが出る。
コンピュータに MSVCR90.dll がないため、プログラムを開始できません。
解決するには 実行ファイルと同じ場所に manifest ファイルを置く。
実際は manifest ファイルをいっしょに配布するのも手間なのでリソースとして実行ファイルに組み込む。
d:/tmp/main.exe
d:/tmp/main.exe.manifest
REFERENCE manifest
■ /Gm
DESC
簡易(最小)リビルド.変更部分のみを取得する
■ _DEBUG(NDEBUG)
DESC
"Debug" 構成で Default ON になっている
"Release" 構成では NDEBUG が有効。
POINT
アサートをリリース時に無効にするには NDEBUG を有効にする。
$vc/include/assert.h には次のようにある
#undef assert
// NDEBUG が ON ならば, assert は何もしない
// NDEBUG を OFF にすると assert にかかる
#ifdef NDEBUG
#else
#endif /* NDEBUG */
■ 言語設定
■ /Ze
VC++ の拡張を禁止する
使用可能にするには
/Ze
使用不能にするには
/Za
■ PreProcessor
POINT
PreProcessor への命令も含まれる
/I< dir> インクルード検索パスに追加
/C コメントを削除しない
/D< name>{=|#}< text> マクロを定義する
/E stdout に前処理する
/EP stdout に前処理する、#line なし
POINT
この手順なら
/EP, /P は不要
SolutionExplorer RMB xxx.cpp > Property
PreProcessor > 前処理済みファイルの生成 > 行番号つきか行番号なしを選択
// 生成
SolutionExplorer RMB xxx.cpp > Compile
■ /D
DESC
マクロ変数を定義する。
コード内で環境によって処理を切り替えるときに使う。
/D "WIN32" /D "TEST"
#ifdef WIN32
inclue < windows.h>
#else
#endif
■ #pragma(指令)
目的:
コンパイラに特定の情報を渡すため指令(pragma) するため
WARNING :
pragma はC++ 標準、
しかし書式、内容はコンパイラ依存により移植性なし
ANSI標準でない
POINT :
#pragma once 多重インクルード防止と同じ意味
主要なコンパイラ(VC、BC,gcc、CodeWarrior)では普通に
#pragma onceが使用できる
■ Warning
/we< n> 警告 n をエラーとして扱う
/wo< n> 警告 n を 1 度だけ表示する
/w< l>< n> n の警告レベル 1-4 を設定する
/W< n> 警告レベルを設定する (既定 n=1)
/Wall 警告をすべて有効にする
/WL 1 行診断を有効にする
■ WX
DESC
警告があった場合にエラー扱いにする。
これを指定するとすべての警告をなくさないと実行ファイルを生成することはできない。
POINT
STL には /W4 警告もあるため
特定の warning だけを無視するには /wd を使う。
ソースコード内にも設定できる。
// W3 に下げる
#pragma warning ( push, 3 )
#include< string>
// 戻す
#pragma warning ( pop )
// 無名の構造体です 警告を無効にする
#pragma warning ( disable : 4201 )
struct {
int i;
} p;
// 元に戻す
#pragma warning ( disable : 4201 )
■ /wd
DESC
指定した Warning を非表示( Disable )にする
■ その他
■ /nologo
DESC
Windows ... みたいな logo を消す。
■ /RTC
DESC
RunTimeCheck ( 実行時のエラーチェックをする )
u : 未初期化変数のチェック
s: スタックフレームのランタイム エラー チェックをする
スタック ポインタの検証によって、スタック ポインタの破損を検出する。
呼び出し規約の不一致によって、スタック ポインタが破損することがある。
たとえば、
関数のポインタを __cdecl として宣言したが、
関数ポインタを使用して __stdcall としてエクスポートされた DLL の関数を呼び出した場合。
23: int cnt;
24: printf( "ret %d\n" , cnt );
0031108E cmp byte ptr [ebp-39h],0
00311092 jne main+91h (3110A1h)
00311094 push offset (311184h)
00311099 call _RTC_UninitUse (311A40h) // チェック用の関数がコールされる。
0031109E add esp,4
003110A1 mov esi,esp
003110A3 mov edx,dword ptr [ebp-28h]
003110A6 push edx
003110A7 push offset ___xi_z+12Ch (315744h)
003110AC call dword ptr [__imp__printf (3182C4h)]
■ EHsc
■ 最適化(Optimize)
/O1 スペースを最小化する
/O2 スペースを最大化する
/Ob< n> インライン展開 (既定値 n=0)
/Og グローバルな最適化を有効にする /Oi[-] 組み込み関数を有効にする
/Os コード スペースを優先する /Ot コードのスピードを優先する
/Ox 最大限の最適化
/Oy[-] フレーム ポインタの省略を有効にする
■ /Ox
DESC
/Ox 最大限の最適化
/Os コード スペースを優先する
■ /Ot
DESC
Code の Speed を優先
■ /Oi
DESC
組み込み関数を有効にする
コンパイラへ
一部の関数呼び出しを組み込み関数に置き換えるように要求する
コンパイラは
関数を呼び出した方が高いパフォーマンスが得られる場合は関数を呼び出すことがある
■ /Od
最適化をオフにする。
コンパイラはソースコードと同じようにアセンブラコードを生成する。
デバッグ時は余計な最適化をかけないように、/Od を指定する。
POINT
混合モードで見るとわかる。
15: int cnt = 0;
16: for( int i=0; i< 5; i++ ){
17: cnt ++;
18: }
19:
20: printf( "ret %d\n" , cnt );
/O2 では cnt が push 5 としてコードが生成される。
002C1C3B push 5
002C1C3D push offset string "ret %d\n" (2C3950h)
002C1C42 call dword ptr [__imp__printf (2C622Ch)]
■ Debug用
■ C7
デバッグをするために利用するシンボルテーブルの形式を設定できる。
C7
MS-DOS 時代からのシンボルテーブルフォーマット
実行ファイルにシンボル情報が組み込まれるためファイルサイズは肥大化する。
WARNING
ファイルサイズが大きくなることと,
インクリメンタルリンクが無効になりリンク回数が増えるため利用しないほうがよい
PDB
PDB フォーマットは一般的に利用されているシンボルテーブルのフォーマット。
実行ファイルとは別の2つのファイルに出力される。
vc90.pdb : 型情報
test.pdb : シンボル情報
POINT
実行ファイルに pdb を含むかどうかは実行ファイル内にある文字列を調べればわかる。
"D:/vc/test/test.pdb" といったパスが埋め込まれている。
bash> strings test.exe | grep "test.pdb"
■ /Z7
DESC
obj file の中に Debug.情報 を組み込む( 古い形式 )
■ /Zi
DESC
デバッグ情報を生成
ProgramDataBase(pdb)を作成する
Debug.情報の形式
型情報は、.obj ではなく .pdb に配置される
/Zi を指定すると /debug が暗黙に指定される
( 内部的に指定したことになる )
POINT
Debug 情報の形式を指定する ( ON/OFF ではない )
Linker の設定もあわせて変更する
Obj file の Debug 情報を pdb file という別ファイルに保存する
POINT
Debug 情報とは 型情報 のこと
cl /Zi e.cpp
ls
// 生成されるもの
e.ilk
e.pdb
vc80.pdb
// まだ作成されない
nm e.exe
■ マニフェスト(manifest) ■ Deprecate
SYNTAX
__declspec(deprecated)
DESC
将来, 非サポートにな予定であることの宣言. ( Compile 時に警告がでる )
POINT
Namespace, Class 名が変更された時は次のようにすると移行の警告を表示可能.
typedef foo::Test DEPRECATED Test;
App 側
Test cam; // CMP は Camera だと認識する
-> ついでに DEPRECATED があるので警告をだす
Function
int DEPRECATED func();
Class
class DEPRECATED Test { ... };
■ インクルード(INCLUDE)探索.INCLUDE_PATH
SYNTAX
#include < path>
#include "path"
DESC
指定されたファイルの内容が挿入する
< >
INCLUDE_PATH を"基点" として検索する
""
File( Source ) を相対として検索
なければ < > と同じ検索
POINT
Library を include する時は < > を利用する
dir
xxx.vcproj
main.cpp
main.h
// main.cpp
// ERROR ( INCLUDE_PATH の指定がない )
#include< main.h>
// OK ( main.h は main.cpp と同じ位置にあるから )
#include"main.h"
// INCLUDE_PATH に vcproj を追加
/I $(ProjDir)
// OK
#include< main.h>
command line から指定するときは
INCLUDE_PATH は cl の実行 Directory の相対になる
test/include/main.cpp
tmp/
foo.h
cd test
cl /I include/tmp include/main.cpp # foo.h へのパスがとおる
cd test
cl /I tmp include/main.cpp # test/tmp となり ERROR
# test/include へ移動
cd include
cl /I tmp main.cpp # test/include/tmp となり OK
■ プリコンパイルヘッダ(PreCompileHeader) ■ マクロ(define.macro)
■ WIN32_LEAN_AND_MEAN
DESC
コンパイラにヘッダーからあまり使われない関数を省くように指示する。
軽くなるしれない。
■ WIN32
DESC
VisualStudio の Project に Default で定義されている
/D WIN32
_WIN32 とは異なる
■ Microsoft 固有のマクロ
DESC
Microsoft 固有というのは Microsoft の Compiler を使ったことのとき
■ _WIN64
DESC
platform x64 で有効になるマクロ
// ポインタ型のサイズを 64 32 bit 環境で切り替える
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef _W64 unsigned int uintptr_t;
#endif
■ _WIN32
DESC
Win32 用アプリケーションに対して定義されます
常に定義されます
Commpiler の CommandLine からは見えない
しかし次のようにチェックすると, 定義されている
// foo.c
#include< stdio.h>
int main()
{
#ifdef _WIN32
printf( "Defined" ); // cl はこちら
#else
printf( "Not Defined" ); // gcc( cygwin はこちら )
#endif
return 0;
}
■ ANSI C標準
■ __FILE__.__LINE__
ファイル名, 行数に置換される
■ __TIME__.__DATE__
日付、時刻に置換される。
■ 定義済みマクロ
VisualStudio ( cl.exe )固有の定義すみマクロは以下のものがある。
■ _WIN32
Win32 用アプリケーションに対して常に定義される。
検証用のコード。
CRT も含めて一切ヘッダをインクルードしないようにする。
#ifdef _WIN32
#error( "_WIN32 defined" )
#endif
int main() {
return 0;
}
ビルドして実行してみる。
cl test.cpp && .\test.exe
■ _MSC_VER
DESC
コンパイラバージョンを定義する。
Visual C++ 6.0 以降に対して 1200 以上として常に定義される。
POINT
また VisualStudio 固有のプラグマを利用する時の環境の判定にも使える。
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning( disable : 4530 )
# pragma warning( disable : 4786 )
#endif
■ デバッグ用マクロ
■ _DEBUG
_ASSERTE などのデバッグ用のマクロ展開の切り替えに利用される。
windows.h などで定義されている デバッグ関数を利用するときは、このマクロを定義する必要がある。
■ NDEBUG
DESC
assert.h 内で assert マクロの展開を制御するためにユーザーが自分で定義するマクロ。
VisualStudio では Release 構成でこのマクロが有効になっている。
アサートを処理を無効化する。
cl /D"NDEBUG" test.cpp
#include< assert.h>
assert( !"test" );
■ 文字コード指定
■ UNICODE
使用する文字の文字コードを unicode に変更する場合に定義するマクロ。
UNICODEを定義してコンパイルすると unicode 用の関数が利用される。
( VisualStudio 2008 環境 )
cl /P /D"UNICODE" test.cpp
MessageBoxW( 0, s, s, 0x00000000L );
cl /P test.cpp
MessageBoxA( 0, s, s, 0x00000000L );
■ _WIN32_WINNT
XP 固有機能を使う
■ WIN32
DESC
Defined for WIN32 applications. Always defined.
Microsoft specific
// gcc -E main.cpp
#include< windows.h>
#ifdef _WIN32
DEFINE _WIN32 // こっちが残る
#else
NOT DEFINE _WIN32
#endif
■ __WINDLL
_WINDLL Windows protected-mode dynamic-link library is
selected with /GD.
■ __MINGW__
DESC
コンパイラ判別用のマクロ。
MinGW 環境で Build すると Define される
コンパイラを識別するために使う
■ __WIN32
cl gcc
__WIN32 ○ ×
WIN32 × ×
■ DLL
DESC
■ __DEBUG
DESC
Debug Build の Default MACRO
stdio.h にも 定義されている
■ _LIB
DESC
StaticLibrary のプロジェクトに設定されるマクロ。
cl -o main.exe ./main.cpp testlib.lib /IMPLIB:test.lib
// obj をつくる ( Compile のみする )
cl /c mod.cpp
// Archive する
lib mod.obj
lib mod.obj /out:mod.lib
// 同一の Symbol があれば ERROR ( と思ったら WARNING )
libA.obj : warning LNK4006:
"void __cdecl subA(void)" (?subA@@YAXXZ) は libB.obj で定義されています。
2 つ目以降の定義は無視されます。
libA.obj : warning LNK4221: パブリック シンボルが見つかりませんでした。アーカイブ メンバにアクセスできません。
lib libA.obj libB.obj
// libA.cpp
void subA() {}
// libB.cpp
void subA() {}
namespace で修飾すれば OK
namespace bar {
namespace foo {
void subA(){}
}
}
// こういう名前で変換される
$nm libB.obj
00000000 T ?subA@foo@bar@@YAXXZ
// obj, lib( obj のセット )は symbol がある
// しかし exe ( 実行 file )は存在しない
// これは Linker の立場からすると 名前引きをしているということ
// 一度, 結合( 完成 )すれば , 名前を書いておく必要がない
// Symbol と Debug 情報は異なる
■ File.構成
.sln
solution
複数の vcproj を内包する
ひとつのsolution は複数のproject をもつことができる
.vcproj
SE > file > import sln|proj
POINT
ProjectFile.proj.はXMLで記述されてので,自由に編集可能, また生成もできる
■ パス
$(BitSuffix) = .64
ProjectName : xxx.proj の名前
OutDir : 出力 Directory
依存 Project がある時は $(OutDir)\lib に出力される
IntDir : 中間ディレクト( Internal Directory )
// /I では FullPath に展開される
$(SolutionDir) xxx.sln
$(ProjDir) xxx.vcproj
/Fo PROJECTNAME
/Fp
/Fd
■ .slnに複数の.projを作成
DESC
file > new > project
POINT
build skip
.sln > RMB > 構成 manager > build off
2. lib の扱い
.vcporj > RMB > proj 依存関係 ON で lib 自動追加
■ 依存関係をもったプロジェクトの構築
DESC
複数の project をもつ solution ファイルは依存関係をつくることができる。
test.sln
app.vcproj // アプリケーション本体
mod.dll // ライブラリとなるモジュール ( dll )
solution には以下の手順で project を追加する。
SolutionExplorer > RMB > 追加 > 新規プロジェクト
> 既存のプロジェクト
作成した後に、依存関係を定義する。
app.vcproj は lib.dll に依存するように指定する。
これによって プロジェクトのビルドの順番の指定と, library( lib ) が自動で追加される
SolutionExplorer > RMB > プロジェクトの依存関係
依存先のプロジェクトの出力ファイルは依存元の 出力フォルダに生成される
Debug/
app.exe
mod.lib
mod.dll
POINT
xxx.lib は __declspec( dllexport ) 宣言がないと出力されない
■ Project 構成
構成 とは Compile するときの"環境設定" ( CommandLine Option )のこと
基本は次の構成
1 Application == 1 Solution ( .sln )
1 Program == 1 Project ( .vcproj )
Application( Solution ) は複数の Program( vcproj )をもつ
Solution
xxx.vcproj
yyy.vcproj
zzz.vcproj
簡単な Program なら 1 Solution , 1 vcproj
■ vsprops
Project 設定を共有する
makefile でいうと makefile_commmon みたいなもの
表示 > PropertyManager
■ その他
■ コンパイル時のエラー
Compiler モジュールがロードされない部分は実態がなくても
コンパイルエラーにはならない
モジュール単位のコンパイルが通れば、link時は結合されないだけ
// foo() はコールされない
void foo(void)
{
bar(); /* 実態はないがコールされないので問題なし(prototype は必要) */
}
■ 使用しないlib について
POINT
不要な Lib があっても問題なし
WARNING :
linker はlib のlistからすべてを開こうとしているので、LIBPATH は必ず追加する
POINT
必要なlib をVC のデフォルトとして設定しておくのあり
[OpenGL32.Lib, etc ...],
次のパスをvcに通しておく
header, lib は全project共通で使用できる
LibPath List
D:\Program Files\Microsoft Platform SDK\Lib\ ( OpenGL32.Lib )
IncludePath List
C:\Program Files\Microsoft Platform SDK\Include( windows.h, OpenGL )
C:\Program Files\Microsoft DirectX SDK (February 2006)\Include ( DirectX )
■ Error.htm.をすぐみる
c-LMB
■ (デバッグ)Debug ■ インラインアセンブラ(Assembler)
■ コンパイルオプションで出力する
main.cpp から main.asm が出力される。
■ インラインアセンブラの基本
SYNTAX
__asm {}
src には固定値, メモリ, レジスタが指定できる。
dst にはレジスタ, メモリ参照を指定する。
Intel CPU は src, dst 共にレジスタ参照はできない。
POINT
メモリ参照をするには [] 内でアドレスを指定する。
アドレスの前には取り出すサイズを指定する。
指定がなければ DWORD PTR が適用される。
POINT
アセンブラの機能ではないが、インラインアセンブラを使う場合は C/C++ で宣言した変数が使える
■ 定数を代入する
mov eax,20 ; eax = 20
mov ebx,0ffh ; ebx = 0xff
■ メモリデータの扱い
// ; eax = *address
mov eax,[address]
mov [address],eax ; *address = eax
■ mov
SYNTAX
mov dst, src
DESC
データを移動する
// ecx, ebx を交換する
mov eax, ecx
mov ebx, ecx
mov ecx, ebx
変数に i に 10 をセットする
■ push.pop
SYNTAX
push src
SYNTAX
pop dst
DESC
スタックにワード, ダブルワードの値をプッシュする。
現在のスタックトップの値をもつ ESP レジスタの値を更新する。
// 固定値, レジスタ, メモリアドレスなどを push できる。
push 10
// push すると ESP レジスタの値が 4 減る
push 20
スタックは LIFO なので push と pop は逆順になる。
push ecx
push edx
pop edx
pop ecx
スワップにも利用できる
push ecx
push edx
pop ecx
pop edx
{{{
{
__asm NOP
}
}}}
{{{
move eax ecx
}}}
■ cmp
SYNTAX
cmp src, dst
DESC
2つのオペランドを比較する。
減算した結果を EFLAGS レジスタの対応するフラグを設定する。
■ movs
SYNTAX
movs dst, src
DESC
ESI レジスタに設定されているアドレスから EDI レジスタに設定されているアドレスへ
データを移動する。
memcpy をする。
mov ESI, src
mov EDI, dst
mov ecx, nr
REP movs BYTE PTR [EDI], BYTE PTR [ESI]
■ プロローグ設定
push EBP
mov EBP, ESP
// ローカル変数用に 20 byte 確保
sub ESP, 20h
■ エピローグ設定
// スタック値の回復
mov ESP, EBP
//
pop EBP
POINT
__cdecl 規約では関数をコールした後にスタックを元の位置に戻すのはコールした側
push 1h
call func
add esp, 4
■ call
SYNTAX
call src
DESC
関数をコールする。
コールした後は呼び出し元に戻る必要があるため、
戻るアドレスをスタックにプッシュする。
そのため呼ばれた側で ESP の値を見れば、戻るアドレスが分る。
// ローカル関数の呼び出しは、目的の関数のアドレスを指定するだけ
int func() {
}
__asm {
call func
}
■ jmp
DESC
絶対アドレスに実行を移す。
// test というラベルに移動する。
jmp test
test:
cmp を使った場合は, JE, JG, JL などを結果に応じて移動できる。
C で if に相当する処理。
// i と 0 を比較する
cmp i 0
JLE less
■ LEA
SYNTAX
lea DST SRC
DESC
src オペランドのアドレスを dst レジスタにセットする。
// int 型の変数 i のアドレスを設定する
int i = 0;
LEA eax, i
// char 型の配列のアドレスを設定する
char buf[256];
LEA ecx, buf
push ecx
call DWORD PTR [GetWindowsDirectory]
int i = 7;
00401016 mov dword ptr [i],7
int &j = i;
0040101D lea eax,[i]
// i のアドレスを j の指す先に 移動する
00401020 mov dword ptr[j], eax
■ inc.dec
int i = 0;
__asm {
inc i
dec i
}
■ fld
SYNTAX
fld
DESC
float 型データの push
■ fstp
SYNTAX
fstp
DESC
float 型データの pop
float i,j;
i=10.0001;
_asm{
// i の値を push
fld i
// j にポップする
fstp j
}
■ fild
SYNTAX
■ fadd
SYNTAX
fadd fD, fA, fB
DESC
float の加算
fAとfBの和をfDに格納する。(倍精度)
■ デバッグ用のビルド
■ プリプロセッサの出力をファイルにはく
マクロの展開に問題が起きているときや, チェックをしたいときに利用する。
int main()
{
return __LINE__;
}
拡張子 .i というファイル( test.i )に展開結果が出力される。
int main()
{
return 11;
}
■ 標準インクルードパスを無視する
/X
INCLUDE などの環境変数を無視して /I で指定したインクルードパスのみを対象にする。
異なるコンパイラが混在した場合に使うと便利。
ビルドエラーになるので, CRT ヘッダのパスを明示的に指定する。
cl /X /I "d:/vc/VC/include/" test.cpp
■ 構造体のアラインメント
/Zp
構造体メンバのアラインメントを指定する。
// pragma でも指定できる。
#pragma pack
■ マップファイルの生成
/MAP
マップファイルを作成する。
シンボル情報をテキストとして保存する。
link /MAP test.obj
link /MAP /MAPINFO:LINES test.obj
■ NODEFAULTLIB
#pragma comment( lib ) で指定されらライブラリのリンクを無視する。
POINT
CRT のヘッダには use_ansi.h のように以下の行があり, リンク時にライブラリを指定しなくても自動でリンクされる。
これを無効にするのが NODEFAULTLIB
#pragma comment(lib,"libcpmt" )
■ /VERBOSE
リンカがシンボルを検索する工程を出力する。
リンカが間違ったシンボルをリンクしているかチェックをしている時に便利。
上の設定では大量のログが出力されるため ファイルだけに絞る。
link /VERBOSE:LIB test.obj
■ 生成される File の種類
■ suo(SolutionUserOption)
DESC
.sln の設定ファイル
■ ncb
DESC
Project DataBase
■ Thread
DESC
Step 実行中は , Thread ごとに Step 処理をしていることになる
複数の Thread で混ざることはない
■ 64bit.用の.CompileOption.の設定をする
以下の設定をすると 64 bit 環境になる
/MACHINE (ターゲット プラットフォームの指定) が /MACHINE:IA64 または /MACHINE:X64 に設定されます
[出力の登録] が無効になります詳細については、「 [リンカ] プロパティ ページ」を参照してください
[ターゲット環境] が /env x64 または /env ia64 に設定されます詳細については
「 [全般] ([MIDL] プロパティ ページ)」を参照してください
[パラメータの確認] が消去され、既定値にリセットされる
詳細については、「 [詳細] ([MIDL] プロパティ ページ)」を参照してください
[デバッグ情報の形式] は、
Win32 プロジェクト構成で /ZI に設定されている場合
64 ビット プロジェクト構成では /Zi に設定されます
詳細については、「 /Z7、/Zi、/ZI (デバッグ情報の形式)」を参照
/D (プリプロセッサの定義)
WIN32 -> WIN64
■ 64.bit環境の作成
USAGE
VC の install において 64 bit 環境を Install する
構成マネージャ > ActiveSolution > 新規 > x64 を選ぶ
■ Expressでの追加インストール
wget http://download.microsoft.com/download/F/1/0/F10113F5-B750-4969-A255-274341AC6BCE/GRMSDKX_EN_DVD.iso
wget http://download.microsoft.com/download/2/E/9/2E911956-F90F-4BFB-8231-E292A7B6F287/GRMSDKX_EN_DVD.iso
以下のようにかいてある。つまりコンパイラも提供してくれる。
The Windows SDK provides
tools, compilers, headers, libraries, code samples, Help
http://d.hatena.ne.jp/torutk/20100927/p1
http://msdn.microsoft.com/ja-jp/library/9yb4317s.aspx
// iso のインストール
http://oshiete.goo.ne.jp/qa/5534393.html
cl コンパイラの場所
/mydata/tool/vc/VC/bin/cl.exe
64bit コンパイラ
/mydata/tool/vc/VC/bin/x86_amd64/cl.exe
GUI から構成を選択できるようにする。
wget http://files.cppblog.com/xcpp/VCE64BIT_WIN7SDK.zip
■ バイナリが64bitか判断する方法
VisualStudio 付属のコマンド dumpbin を使うと確認できる。
FILE HEADER VALUES の最初の項目(machine)がx86かx64かで区別する。
dumpbin /HEADERS main.exe
FILE HEADER VALUES
14C machine (x86)
3 number of sections
FILE HEADER VALUES
8664 machine (x64)
■ WOW64
WOW64 とは 64bit 環境で 32bit アプリケーションを実行するためのエミュレータのこと。
WARNING
64 bit アプリケーションではポインタの扱いに注意が必要。
32 ビット アプリケーションでは、IsWow64Process() を呼び出すことによって
実行されているのが WOW64 環境なのかわかる。
BOOL b;
IsWow64Process( GetCurrentProcess(), &b );
■ Keybind
DESC
Tool > Option > 環境 > Keyboard
keyboard map scheme 選択 > 名前をつけて保存
command list を選択
ショートカットキー box に アサインしたい key を打つ
s- : Shift +
c- : Control +
m- : Alt +
BIND
c-l : cp line
c-s-l : kill line
c-c : cp line
c-i : IncrementSearch ( c-f : Insensitive | Sensitive )
m-Drag : Rect Sel
s-m-Drag
c-k, c-c : Comment Sel
c-k, c-u : unComment Sel
c-r | c-w: Space 表示
c-\ | c-t: task 一覧表示 //TODO コメント
c-m-b : BreakPoint 一覧表示.
f12 : throw Exception
W: インクリメンタル検索
F: VisualC++ document
Edit.Find : c-f : 検索
Edit.FindinFiles: c-s-f : file 内検索
Edit.FindNext : f3 : 次の検索箇所
Edit.FindPrev : s-f3 : 前の検索箇所
Edit.Replace : c-h : 置換
Edit.ReplaceInFile : c-s-h : ファイル内置換
Edit.isearch : c-i : インクリメンタルサーチ
Edit.r-isearch : c-i : 逆インクリメンタルサーチ
定義へ移動 : f12
戻る : c-f6
参照へ移動 : s-f12
宣言へ移動 : c-f12 -> (c-b : c-b に変更)
symbol 検索: m-f12
tab 移動 : c-tab
breakpoint : f9
■ WindowsLibrary
DESC
普段 無意識で LINK されているので, ちょいとメモ
Project Default の値として 継承される
kernel32.lib
■ kernel32.lib
D:/Program Files/Microsoft Platform SDK/Lib/Kernel32.Lib
cd D:/Program Files/Microsoft Platform SDK/Lib/
nm kernel32.lib | grep " T .*@"
KERNEL32.dll:
00000000 I .idata$4
00000000 I .idata$5
00000000 I .idata$6
00000000 T .text
00000000 T _CreateDirectoryExA@12
U __IMPORT_DESCRIPTOR_KERNEL32
00000000 I __imp__CreateDirectoryExA@12
■ DLL2
■ Export.Import
DLL 内の関数を他のプログラムから使用できるようにすること
使う側:
// DLL から LINK するため LINK ERROR しないように指示をする。
__declspec( dllimport )
逆に Client 側から利用してもらうには, __declspec(dllexport) を宣言して
Symbol 情報を出力する
この設定をしないと .lib が出力されない
存在理由
LINKER に DLL 内に関数があることを知らせるため
名前を互いに明示的に表示することで
アクセスできるようにする.
EXport とは
dll をテキストエディタやバイナリエディタで開くと
中に関数名が文字列として書かれてる
これが「見える」ということで、検索可能であるということ。
static lib でも 関数名が見える。見えないと LINK できない。
DLL 版の .lib size は小さい
しかも .lib には 文字列などの情報がない
複数の DLL を同一関数名で
暗黙LINK すると 先に lib に指定した方で Symbol を解決する
明示版でも同じはず
■ 使い方
declspec(dllexport)キーワードを使用する
( MS固有の予約語、ANSIC にはない )
__declspec(dllexport) func();
__declspec(dllimport) func();
2. def file を利用する
POINT
関数名ではなく序数で公開するので、DLLのサイズが小さくなる
以下の項目をファイルに記述
1. ライブラリ名
2. エクスポートする関数名
LIBRARY DllTest
DESCRIPTION "DLL-TEST"
EXPORTS
calcTest @1
stringTest @2
■ dll のリンク方法
暗黙(静的)リンク : GetProcAddress() の代わりに lib を LINK する
-> WARNING : 複数の DLL で同一の Symbol を利用する場合は不要.
関数の宣言
__declspec(dllimport) type func(type); // VC 固有なので注意
2. linker
.lib を link
.lib に関数名と序数が記述
3. DLL のロード
実行の準備前にロード
4. 関数とのbind
.lib に記述した関数がないと実行時エラー
5. 暗黙
pro がコードに記述しなくても, load されるので暗黙的
6. 静的
linker がリンクした時点で, どの関数を使用するかが
決定するので静的
■ 明示(動的)リンク
関数の宣言、DLL の関数の宣言はしない
( 関数ptr のtypedef はあると便利 )
2. linker
linker のリンクは不要
( 実行時に動的にコール )
3. DLL のロード
LoadLibrary() でロード
4. 関数とのbind
GetProcAddress で関数名( 序数 )を指定することで関数ポインタを取得
5. 明示
pro がLoadLibrary() するので 明示的
6. 動的
LoadLibray, GetProcAddress で自由にコールできるので動的
■ DLL
DESC
プログラムの実体をもつfile
関数名 | 序数 でエントリを保持
POINT
暗黙
.lib のエントリ情報を元に関数を bind
明示
GetProcAddress() で明示的に指定する。
POINT
# dll 内の関数名、序数が表示する
VC Cmd > dumpbin /exports foo.dll
# dll 内の依存関係をしらべる
# この DLL(Exe) を使うのに必要な DLL を表示
# たとえば LINK する xxx.lib を変更すると内容もかわる
VC Cmd > dumpbin.exe /dependents foo.dll
■ DumpBin
DESC
実行ファイルから Symbol を出力するツール。
外部 の library でどのような関数があるか, 調べる際に利用する.
VS の 関数名で BreakPoint をしかけると併用すると便利.
// すべてを見るには
dumpbin /all foo.exe
// printf() でも
MSVCR80D.dll に依存する
export している関数を調べる。
次のように出力される。
Section contains the following exports for test.exe
00000000 characteristics
4F366DB1 time date stamp Sat Feb 11 22:31:29 2012
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 00001040 ?testexport@@YAHXZ
■ 使い分け
暗黙
プログラムが楽( headerを細工するだけで良い )
明示
DLL の有無を問わない
特定の dir の DLL をロードする
DLL file 名を定義ファイルに記述して、ロードできる。
■ .lib
DESC
DLL 本体への入り口( アドレス )が記述されたファイル
入り口 == ( 関数名 | 序数 )
DLL load 時に .lib 内の関数をすべて ロードする
■ 序数
DESC
dll の関数をあらわす一意の数字のこと
POINT
EXport する関数の数がかわると、DLL がもつ 序数が変化する
新たな .lib をリンクする必要がある
■ def
DESC
dll に保持される、関数名、序数を定義するファイル
SYNTAX
; 関数名と序数を指定
EXPORTS
testfunc @1
foofunc @2
POINT
def file を使用する際は dllexport する必要はない
逆に dllexport することが、def file ですることを自動化している。
DLL, .lib に export 情報を組み込むこと意味
■ その他
■ header に関数名があるから lib は link 場合でも必要
DLL 本体への入り口 ( 関数名 , 序数 ) があるので 必須
関数宣言だけで DLL 内のどこを見ればよいか分らない。
■ dll lib file 名を変えることはできない。
A. lib ならば変更できる。
-> lib には [ この dll の この関数 ] という情報がある
[ この dll ]は lib に記述すみ
■ __declspec(dllexport)
DESC
DLL とは次の三つで構成される
export : DLL として公開する symbol list
2. code, data : 公開する関数、変数の本体
3. import library : DLL の symbol を link するための
OS への最低限の情報
以上のことを
export は text editor で list を手書きでつくるが
自動でやってくれるのが__declspec(dllexport)
■ デバッグCRTライブラリ(DebugLibrary)
VisualC++ にはデバッグライブラリがあるため
メモリリークなどを検出できる。
メモリリークを検出するには次のマクロを定義することで、実装する処理を切り替える
#define __CRTDBG_MAP_ALLOC
#include< crtdbg.h>
int main() {
// 利用したいデバッグ機能をフラグで指定する。
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
void *p = malloc( 256 );
// プログラムの終了時にリークをチェックする。
_CrtDumpMemoryLeaks();
return 0;
}
ビルド設定は次のようにする。
デバッグ版のCRTを利用するため, コンパイル時のオプションで /MTd を選択する必要がある。
cl /c -D_DEBUG /MTd -Od -Zi -W3 test.cpp && link -verbose:lib /debug test.obj
// BAD
cl /c -D_DEBUG /MT -Od -Zi -W3 test.cpp && link -verbose:lib /debug test.obj
WARNING
_CrtDumpMemoryLeaks のログは OutputDebugString() として出力されるため
コンソールへの stdout, stderr には出力されない。
■ _CrtSetDbgFlag
SYNTAX
int _CrtSetDbgFlag(
int newFlag
);
DESC
デバッグ用のヒープマネージャのオプションを指定する。
_CRTDBG_ALLOC_MEM_DF
デバッグヒープ割り当てを有効にする。
_CRTDBG_LEAK_CHECK_DF
プログラムの終了時に _CrtDumpMemoryLeaks を呼び出して、メモリ リークのチェックを自動的に実行する。
■ _CrtSetReportMode
SYNTAX
int _CrtSetReportMode(
int reportType, タイプ ( _CRT_WARN _CRT_ERROR _CRT_ASSERT )
int reportMode 出力先
);
DESC
_CrtDbgReport、_ASSERT マクロ、_ASSERTE マクロ、_ASSERT マクロ、_ASSERTE マクロ、
_RPT、_RPTF、_RPTW、_RPTFW のマクロ、_RPT、_RPTF、_RPTW、_RPTFW のマクロ などの
_CrtDbgReport、_CrtDbgReportW
を呼び出すすべてのマクロが生成する特定のレポートの種類の出力先を指定する。
(デバッグ バージョンのみ)
_CRTDBG_MODE_DEBUG
メッセージをデバッガの出力ウィンドウ
_CRTDBG_MODE_FILE
ユーザー指定のファイル ハンドル
_CrtSetReportFile を呼び出して、出力先となるファイルまたはストリームを定義します。
_CRTDBG_MODE_WNDW
メッセージ ボックスを作成し
メッセージと Abort、Retry、Ignore の各ボタンを表示する。
_CRTDBG_REPORT_MODE
指定された reportType の reportMode を返します。
アサートの出力を stderr とダイアログボックスに表示する。
_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW );
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
_ASSERTE( !"test" );
■ _CrtSetReportFile
SYNTAX
_HFILE _CrtSetReportFile(
int reportType,
_HFILE reportFile
);
DESC
stderr にアサート結果を出力する。
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
HANDLE h = CreateFile("c:\\log.txt" , GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, h);
_RPT0(_CRT_WARN,"file message\n" );
CloseHandle(h);
■ _CrtSetDumpClient
SYNTAX
_CRT_DUMP_CLIENT _CrtSetDumpClient(
_CRT_DUMP_CLIENT dumpClient
);
WARNING
これは _CLIENT_BLOCK 型なので _NORMAL_BLOCK 型のリークにはフックできない。
DESC
アプリケーションは、_CLIENT_BLOCK メモリ ブロックに格納されているオブジェクトをダンプする独自の関数を
C ランタイムデバッグのメモリ ダンププロセスにフックできる
この結果、_CrtMemDumpAllObjectsSince や _CrtDumpMemoryLeaks などのデバッグ ダンプ関数が
_CLIENT_BLOCK メモリ ブロックをダンプするたびに、
アプリケーションのダンプ関数も呼び出される。
_CrtSetDumpClient を使用すると
簡単な方法でメモリ リークを検出し、_CLIENT_BLOCK ブロックに格納されているデータのコンテンツを検証またはレポートできます。
_DEBUG が未定義の場合、_CrtSetDumpClient の呼び出しはプリプロセスで削除される。
_CrtSetDumpClient は、dumpClient で指定されたアプリケーション定義の
新しいダンプ関数をインストールし、前回定義されていたダンプ関数を返す。
クライアント ブロックのダンプ関数の例は、次のとおりです。
void DumpClientFunction(
void *userPortion,
size_t blockSize
);
userPortion
メモリブロックのユーザー データ領域の先頭へのポインタです。
blockSize
割り当てられたメモリ ブロックのサイズをバイト単位で指定する。
クライアント ブロックのダンプ関数は、void を返す必要がある。
_CrtSetDumpClient に渡されるクライアント ダンプ関数へのポインタは
_CRT_DUMP_CLIENT 型で、Crtdbg.h で次のように定義される。
typedef void (__cdecl *_CRT_DUMP_CLIENT)( void *, size_t );
■ _CrtDumpMemoryLeaks
SYNTAX
int _CrtDumpMemoryLeaks();
DESC
メモリ リークの発生時に、デバッグ ヒープ内のメモリ ブロックをすべてダンプする。
RET
true : リークがある
false : リークなし
■ _CrtSetBreakAlloc
SYNTAX
long _CrtSetBreakAlloc(
long lBreakAlloc // BP を設定する割り当て順序番号
);
DEP
< crtdbg.h>
DESC
アプリケーションは、特定のメモリ割り当て位置にブレークポイントを設定し、
要求元までトレースすることによって、メモリ リークを検出できます
使用されるオブジェクト割り当て順序番号は、ヒープへの割り当て時にメモリブロックに割り当てられた
シーケンシャル番号です
_DEBUG が未定義の場合、_CrtSetBreakAlloc の呼び出しはプリプロセスで削除されます
オブジェクト割り当て順序番号は
Crtdbg.h で定義されている _CrtMemBlockHeader 構造体の lRequest フィールドに格納されます
デバッグ ダンプ関数のいずれかが出力するメモリ ブロック情報のレポートでは
中かっこで囲まれて {36} のように表示されます
■ _CrtIsValidHeapPointer();
POINT
Debug Library のみにあるため, /MTd にしないと LinkError
つまり Option を切り替えることでリンクする Library を変更しているということ
dll 内の heap を解放すると crash する
参照するだけなら 問題ない。
char *p = testdll();
_ASSERTE( _CrtIsValidHeapPointer(p) );
// これで落ちる
delete p;
■ アサート(Assert)
assert は ANSI C 標準マクロとして assert.h に定義されている。
VisualStudio 環境では, Console アプリケーションは stderr に出力されて
GUI アプリケーションでは メッセージボックスが出力される。
POINT
1つのアサートに対して1つのチェックをすること。
assert( p != NULL );
// これでは どちらのチェックに失敗したか不明。
assert( p != NULL && cnt > 0 );
自前でマクロを再定義しておくと便利。
デバッグしやすいように、できるだけ情報( 式, メッセージ, ファイル、行 )を出力しておく。
do - while(0) をするのは if() assert() とあった場合の対策。
void assertmessage( const char *exp, const char *msg, const char *file, int line )
{
printf("ASSERT FAIL: MESSAGE[%s] EXP[%s] FILE[%s] LINE[%d]" , msg, exp, file, line );
}
#undef assert
#define assert( cond, msg ) \
do { \
if ( !(cond) ) { \
assertmessage( #cond, msg, __FILE__, __LINE__ ); \
DebugBreak(); \
} \
} while (0)
デバッグ以外のときはアサートは無効にする。
#ifdef _DEBUG
# define assert( x ) do{ }while(0)
#else
# define assert( x )
#endif
■ _ASSERT(_ASSERTE)
assert は ANSI C 標準マクロであり, _ASSERT, _ASSERTE は VisualStudio 固有のアサートマクロ。
_ASSERTE は Expression(アサート式)を表示してくれるため、こちらの方がデバッグ時に役にたつ。
#include< crtdbg.h>
_ASSERTE( !"test" );
マクロの展開結果を見てみると、
ファイル名, 行, 式が _CrtDbgReport() に渡されていることがわかる。
cl /D"_DEBUG" /P test.cpp
(void) ((!!((!"test" ))) || (1 != _CrtDbgReportW(2, L"test.cpp" , 79, 0, L"!\" test\"" )) || (__debugbreak(), 0));
■ クラッシュハンドラ
アプリケーションエラーを表示する前に、アプリケーションのロジックを制御してしまう。
■ IsBadCodePtr
SYNTAX
BOOL IsBadCodePtr(
FARPROC lpfn // メモリのアドレス
);
RET
0 : 有効
!0 : 不正なポインタ
DESC
呼び出し側プロセスが
指定されたアドレスのメモリに対して読み取りアクセスができるか返す。
POINT
NULL チェック以外にバッファオーバーランによって不正なアドレスを指すポインタも検知できる。
{
int *ptr = (int *)1;
// int i;
// ptr = &i;
assert( IsBadCodePtr( (FARPROC)ptr ) == 0 );
}
kernel32.lib
■ OutputDebugString
DESC
現在のアプリケーションのデバッガに文字列を送信する。
■ CRT
インポート関数とは外部 DLL から公開されている関数のこと。
C 標準ライブラリでも DLL からロードすることを選択できる。
コンパイラは実際にジャンプする関数のアドレスではなく
間接アドレスにジャンプするコードを生成する。
/MD, /MT を変更すると混合モードでのアセンブラ表示で call 命令の引数が変わる。
00401018 call dword ptr [__imp__printf (406220h)]
00401018 call printf (401029h)
MessageBox などの WindowsAPI は /MT, /MD の設定によらず常に間接参照を使っての
DLL内の関数呼び出しであることがわかる。
00401034 call dword ptr [__imp__MessageBoxA@16 (4253DCh)]
dumpbin /imports を使うと Import Adress Table のアドレスがわかる。
MSVCR90.dll
4061FC Import Address Table
4060B8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
10B _adjust_fdiv
CB __p__commode
CF __p__fmode
16A _encode_pointer
E0 __set_app_type
43 ?terminate@@YAXXZ
E3 __setusermatherr
96 __dllonexit
276 _lock
31C _onexit
160 _decode_pointer
173 _except_handler4_common
20B _invoke_watson
13F _controlfp_s
14B _crt_debugger_hook
13C _configthreadlocale
205 _initterm_e
204 _initterm
A0 __initenv
4CC exit
66 _XcptFilter
17C _exit
12C _cexit
9F __getmainargs
115 _amsg_exit
52E printf
■ VirtualQuery
SYNTAX
DWORD VirtualQuery(
LPCVOID lpAddress, // 領域のアドレス
PMEMORY_BASIC_INFORMATION lpBuffer, // 情報バッファのアドレス
DWORD dwLength // バッファサイズ
);
呼び出し側のプロセスの仮想アドレス空間にある範囲のページの情報を取得する。
指定されたアドレスから始まる連続ページのうち
次の属性で共通しているページに関する情報が取得できる。
ほかのプロセスのアドレス空間にある範囲のページに関する情報を取得するには
VirtualQueryEx() を使う。
■ VirtualProtect
SYNTAX
■ VirtualProtect
SYNTAX
BOOL VirtualProtect(
LPVOID lpAddress, // コミット済みページ領域のアドレス
DWORD dwSize, // 領域のサイズ
DWORD flNewProtect, // 希望のアクセス保護
PDWORD lpflOldProtect
// 従来のアクセス保護を取得する変数のアドレス
);
呼び出し側のプロセスの仮想アドレス空間内のコミット済みページ領域に対する
アクセス保護をの状態を変更する。
コミット済みページに限ってアクセス保護値を設定できる。
指定された領域内のページがコミット済みではない場合、この関数は失敗し
どのページのアクセス保護も変更しない。
■ MakePtr
■ Debugger
コンソールプログラムならは Control-c で DBG_CONTROL_C 例外を発生させる。
76FF15DE add esp,4
混合ウィンドウでアドレスを見ると問題の箇所がわかる。
test.exe の 0x76ff15de でハンドルされていない例外が発生しました:
0xC0000005: 場所 0x00000000 を読み込み中にアクセス違反が発生しました。
例外の種類には以下のものがある
■ WaitForDebugEvent
SYNTAX
BOOL WaitForDebugEvent(
LPDEBUG_EVENT event, // デバッグイベントの情報が入った構造体へのポインタ
DWORD time // 待つ時間 ( msec )
);
DESC
デバッグ中のプロセスでデバッグイベントが発生するのを待つ。
WaitForDebugEvent 関数を呼び出すことができるのは
デバッグ中のプロセスを作成したスレッドだけ。
■ ContinueDebugEvent
SYNTAX
BOOL ContinueDebugEvent(
DWORD dwProcessId, // 続行するプロセス
DWORD dwThreadId, // 続行するスレッド
DWORD dwContinueStatus // 続行状態
);
デバッグイベントを報告したスレッドをデバッガが続行できるようにします。
OS に継続を通知する。
■ CreateProcess
SYNTAX
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 実行可能モジュールの名前
LPTSTR cmdline, // コマンドライン文字列
LPSECURITY_ATTRIBUTES lpProcessAttributes, // セキュリティ記述子
LPSECURITY_ATTRIBUTES lpThreadAttributes, // セキュリティ記述子
BOOL bInheritHandles, // ハンドルの継承オプション
DWORD dwCreationFlags, // フラグ
LPVOID lpEnvironment, // 新しい環境ブロック
LPCTSTR sDir, // カレントディレクトリの名前
LPSTARTUPINFO pStartupInfo, // スタートアップ情報
LPPROCESS_INFORMATION pProcessInformation // プロセス情報
);
DESC
DESC
新しい 1 個のプロセスと、そのプライマリスレッドを作成
Windows OS の元では
実行ファイルを開くことはプロセスの生成と同じ
任意のプログラムをコマンドラインで、あるいは引数付で実行することができます
実行開始時の状態を指定したり実行したプロセスやプロセスのメインスレッドのハンドルも得られるので
プログラムの終了を待つなど柔軟な制御ができる
適当なエディタ機能とCreateProcess()を組み合わせれば
「編集したソースをコマンドラインでコンパイラに渡してコンパイル、コンパイル終了を待って実行」
という簡単な「統合開発環境」を開発することもできる
lpProcessAttributes
構造体で子プロセスが
取得したハンドルを 継承 できるかどうかを指定する
NULL を指定すると
取得したハンドルを継承できない
ノートパッドを起動する。
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb=sizeof( si );
CreateProcess(
NULL,
"notepad" ,
NULL,NULL,
FALSE,
NORMAL_PRIORITY_CLASS, // Thread Schedular の 優先順位 は普通
NULL,NULL,
&si,&pi
);
コマンドライン文字列は引数も含めてすべてを指定する。
コンソールウィンドウを新規に与えるには CREATE_NEW_CONSOLE フラグをたてる
CreateProcess(
NULL,
"ping localhost" ,
NULL,NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,NULL,
&si,&pi
);
プロセスの終了まちをする。
WaitForSingleObject( pi.hProcess, INFINITE );
フラグは次のようなものを指定できる
CREATE_NEW_CONSOLE
親のコンソールを継承せず新しいコンソールを持ちます。DETACHED_PROCESS フラグと同時に指定することはできません。
CREATE_NO_WINDOW
コンソールアプリケーションを起動する場合にのみ有効です。
このフラグを指定すると、コンソールウィンドウなしでアプリケーションを実行する。
DEBUG_PROCESS
呼び出し側プロセスをデバッガ、新しいプロセスをデバッグ対象として扱う。
OS は、デバッグ対象のプロセス内で発生するすべてのデバッグイベントを呼び出し側スレッドへ通知します。
このフラグを指定してプロセスを作成すると
呼び出し側スレッド(CreateProcess 関数を呼び出したスレッド)だけが 関数を呼び出せます。
DEBUG_ONLY_THIS_PROCESS
呼び出し側プロセスがデバッグ対象であるときに、
このフラグを指定せずに CreateProcess 関数を呼び出すと
呼び出し側プロセスを扱っているデバッガは、新しいプロセスもデバッグ対象とします。
呼び出し側プロセスがデバッグ対象ではない場合、デバッグ関連の動作は発生しません。
プロセスの実行でブロックされるため、スレッドで CreateProcess を実行すると
ユーザーの応答をうけることができる。
DWORD WINAPI func(LPVOID lpArg) {
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb=sizeof(si);
CreateProcess(NULL,(LPTSTR)lpArg,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,
NULL,NULL,&si,&pi);
CloseHandle(pi.hThread);
// 終了まち
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle(pi.hProcess);
PostMessage(hwMain,WM_USER,0,0);
ExitThread(0);
return 0;
}
■ breakpoint
POINT
ブレークポイントを仕込むには、インストラクションポインタの場所に
ブレークポイント命令を入れるだけ。
Intel 系 CPU では ニーモニックは INT 3 。
オペコードは CC。
POINT
デバッガでブレークポイントを仕込むにはこのオペコードで上書きしてしまえばいい。
■ OpenProcess
SYNTAX
HANDLE OpenProcess(
DWORD dwDesiredAccess, // アクセスフラグ
BOOL bInheritHandle, // ハンドルの継承オプション
DWORD id // ハンドルを取得したいプロセスのID
);
DESC
既存のプロセスオブジェクトのハンドルを開く。
■ GetCurrentThread
SYNTAX
■ GetThreadContext
SYNTAX
BOOL GetThreadContext(
HANDLE hThread, // 情報を取得したいスレッドのハンドル
LPCONTEXT lpContext // コンテキスト情報
);
RET
0 : 失敗
!0 : 成功
DESC
指定したスレッドのコンテキスト( レジスタ( ISP, ESP )の値など )を取得する。
WARNING
実行中のスレッドには利用することができないので, SuspendThread() を使ってとめる。
情報を取得するには スレッドをとめる必要がある。
スレッドへの THREAD_GET_CONTEXT アクセス権が必要。
CONTEXT stCtx ;
stCtx.ContextFlags = CONTEXT_FULL ;
GetThreadContext ( GetCurrentThread ( ) , &stCtx ) );
// レジスタの値がとれる。
stCtx.Eip;
stCtx.Esp;
stCtx.Ebp;
■ スタックトレース(StackTrace)
シンボルテーブル情報があればスタックトレースをできる。
#include< imagehlp.h>
#pragma comment(lib, "imagehlp.lib" )
LONG CALLBACK SWFilter(EXCEPTION_POINTERS *ExInfo)
{
STACKFRAME sf;
BOOL bResult;
PIMAGEHLP_SYMBOL pSym;
DWORD Disp;
printf("例外発生\n" );
// シンボル情報格納用バッファの初期化
pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, 10000);
pSym->SizeOfStruct = 10000;
pSym->MaxNameLength = 10000 - sizeof(IMAGEHLP_SYMBOL);
// スタックフレームの初期化
ZeroMemory(&sf, sizeof(sf));
sf.AddrPC.Offset = ExInfo->ContextRecord->Eip;
sf.AddrStack.Offset = ExInfo->ContextRecord->Esp;
sf.AddrFrame.Offset = ExInfo->ContextRecord->Ebp;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Mode = AddrModeFlat;
// シンボルハンドラの初期化
SymInitialize(GetCurrentProcess(), NULL, TRUE);
// スタックフレームを順に表示する
for(;;) {
// 次のスタックフレームの取得
bResult = StackWalk(
IMAGE_FILE_MACHINE_I386,
GetCurrentProcess(),
GetCurrentThread(),
&sf,
NULL,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
// 失敗ならば、ループを抜ける
if(!bResult || sf.AddrFrame.Offset == 0) break;
// プログラムカウンタから関数名を取得
bResult = SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &Disp, pSym);
// 取得結果を表示
if(bResult) printf("--- 0x%08x %s() + 0x%x\n" , sf.AddrPC.Offset,
pSym->Name,
Disp);
else printf("%08x, ---" , sf.AddrPC.Offset);
}
// 後処理
SymCleanup( GetCurrentProcess() );
GlobalFree( pSym );
return(EXCEPTION_EXECUTE_HANDLER);
}
■ StackWalk
SYNTAX
BOOL StackWalk(
DWORD MachineType, // IMAGE_FILE_MACHINE_I386 ;
HANDLE hProcess,
HANDLE hThread,
LPSTACKFRAME StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, // 関数ポインタ
PTRANSLATE_ADDRESS_ROUTINE TranslateAddress // NULL
);
■ SymSetOptions
SYMOPT_LOAD_LINES : 行番号をロードする
SymSetOptions ( dwOpts | SYMOPT_LOAD_LINES ) ;
■ SymInitialize
BOOL SymInitialize(
HANDLE hProcess,
PSTR UserSearchPath, // シンボル情報ファイルの検索パス
BOOL fInvadeProcess
);
RET
TRUE : 成功
FALSE : 失敗
DESC
プロセス用のシンボルハンドラを初期化する。
シンボルハンドラの観点では、
プロセスはシンボル情報を収集するときに利用できる便利なオブジェクトです。
シンボルハンドラを利用するのは、
デバッグ対象のプロセス用のシンボルをロードする必要のあるデバッガや他のツールです。
HANDLE hProcess = GetCurrentProcess () ;
if ( FALSE == g_cSym.SymInitialize ( hProcess ,
NULL ,
FALSE ) )
if ( GetThreadContext ( GetCurrentThread ( ) , &stCtx ) )
■ GlobalAlloc
SYNTAX
HGLOBAL GlobalAlloc(
UINT flag, // 割り当て方法
DWORD sz // サイズ
);
RET
NULL : 失敗
DESC
指定された Byte 数を Heap から割り当てる。
POINT
「固定メモリ」と「移動可能メモリ」の二種類のメモリを割り当てる機能がある。
固定メモリ
割り当てたメモリ領域のアドレスが返される
移動可能メモリ
メモリ領域を識別するハンドルが返され、メモリ領域自体は任意に移動される
移動可能メモリはvmem機構の存在しなかったWin16の時代の名残。
vmem機構の備わったWin32環境ではほとんど意味はない。
ただし、DDEやクリップボードなどへのデータの送受信など
特定の目的で使用する場合はある。
// 固定 Memory Address として割り振る
// Win32 では 物理 Memory の 移動, Swap はされる
// 理論上は 固定 ということ
//
GlobalAlloc( GMEM_FIXED, sz );
// 仮想 Memory 空間を移動できる
GlobalAlloc( GMEM_MOVEABLE, sz );
■ SymGetSymFromAddr
SYNTAX
BOOL SymGetSymFromAddr(
HANDLE hProcess,
DWORD Address, // 検索したいシンボルのアドレス( シンボルの境界でなくてもかまいません。
アドレスがシンボルの先頭とシンボルの終わり
( シンボルの先頭にシンボルサイズを足した位置)の間にある場合そのシンボルが見つかる)
PDWORD Displacement, // シンボルの先頭からの変位( オフセット)、または 0 を指定します。
PIMAGEHLP_SYMBOL Symbol // シンボル名を受け取る構造体
);
DESC
プログラムカウンタから関数名を取得する。
指定されたアドレスに置かれているシンボルを検索します。
■ SymGetLineFromAddr
SYNTAX
BOOL SymGetLineFromAddr(
HANDLE hProcess,
DWORD dwAddr,
PDWORD pdwDisplacement,
PIMAGEHLP_LINE Line
);
DESC
指定されたアドレスに対応するソースコードの行、ファイル名を検索する。
SymGetLineFromAddr を呼び出すに
Line バッファを割り当て、 構造体の必要なメンバ( サイズなど )をセットすること。
IMAGEHLP_LINE stIHL ;
ZeroMemory ( &stIHL , sizeof ( IMAGEHLP_LINE ) ) ;
stIHL.SizeOfStruct = sizeof ( IMAGEHLP_LINE ) ;
if ( 0 != SymGetLineFromAddr ( dwAddr ,
&dwDisp ,
&stIHL ) )
{
// Put this on the next line and indented a bit.
pCurrPos += wsprintf ( pCurrPos ,
_T ( "\n\t\t%s, Line %d" ) ,
stIHL.FileName ,
stIHL.LineNumber ) ;
// オフセットがあれば
if ( 0 != dwDisp )
{
pCurrPos += wsprintf ( pCurrPos ,
_T ( " + %d bytes" ) ,
dwDisp ) ;
}
}
■ SymCleanup
SYNTAX
BOOL SymCleanup(
HANDLE hProcess
);
DESC
プロセスハンドルに関連付けられているすべてのリソースの割り当てを解除する。
■ ReadProcessMemory
SYNTAX
BOOL ReadProcessMemory(
HANDLE hProcess, // プロセスのハンドル
LPCVOID lpBaseAddress, // 読み取り開始アドレス
LPVOID buf, // データを格納するバッファ
DWORD size, // 読むバイト数
LPDWORD lpNumberOfBytesRead // 読み取ったバイト数
);
DESC
指定されたプロセスのアドレス空間から指定された範囲のデータを
現在のプロセスの指定されたバッファへコピーします。
PROCESS_VM_READ アクセス権付きのハンドルを備えている任意のプロセスは
この関数を呼び出せます。
通常は、デバッグ中のプロセスを読み取り対象にする。( それ以外にも使ってもよい。 )
POINT
プロセスのハンドルは自身でも良いし、別のプロセスでも良い。
デバッガがデバッグ対象のプロセスの仮想メモリ空間の値を変更するために使う。
■ WriteProcessMemory
■ SetUnhandledExceptionFilter
SYNTAX
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER callback
);
DESC
Win32 が各スレッドとプロセスの最上位に置くトップレベル例外ハンドラを呼び出し元アプリケーションに置き換えます
この関数の呼び出し後
デバッグ中でないプロセスで例外が発生し
その例外が Win32 未処理例外フィルタに到着すると、
そのフィルタによって、callback で指定した例外フィルタ関数が呼ばれる。
コールバックされる関数は次のシグネーチャをもつ
LONG CALLBACK SWFilter( EXCEPTION_POINTERS * );