コンソールプログラムならは 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 * );