Heliodor 2018/09/28 19:12

たまにはプログラムの話2

前回、ヴィータ大脱出での不正終了イベントログの取得について書きましたが、あれって結構不格好な実装だったんですよ。なんたって、不正終了した時にそのイベントを取得できるのは次回起動時でしたからね。不正落ちしてそのまま電源を切って、翌日起動とかされると、15分制限に引っかかってイベント取得できなかったわけです。

かといってイベントの検索範囲を24時間とかにするとイベントの量が多くなり過ぎて検索に時間が掛かる=起動に時間が掛かりますし、どうせなら不正終了したその場でイベントを取得してテキストファイルに書き出してダイアログ表示したいじゃないですか。

そこで、もう一工夫して一度はボツにした SetUnhandledExceptionFilter を使ってみることにしました。
SetUnhandledExceptionFilter を使った時の問題点は、例外検出しても、その時点ではまだエラーイベントが作成されていないため、詳しいエラー情報を取得できないというものでした。ならば、アプリが完全に終了するまで待ってやればよいではないかと。


というわけで。次のように処理内容を変更しました。

  • ゲーム起動時に SetUnhandledExceptionFilter で例外フックを仕掛けておく
  • 例外フックが発動したら、自分のプロセスIDをコマンドラインパラメータにしてプログラムBを起動する
  • そのまま終了させる

  • プログラムBは起動時にコマンドライン引数を調べ、例外発生側のプロセスIDが指定されていれば、そのプロセスが終了するまで待機

  • 終了を確認したらWindowsのイベントログを調べる
  • 該当エラーイベントが見つかったら内容をテキストファイルに書き出してダイアログを出す

ここで、プログラムBとは誰ぞや?ということですが、これはゲームプログラム自身です。
ただのゲームとして起動されたのか、それとも例外報告用として起動されたのかはコマンドラインパラメータで識別します。

全体の流れは以下の通りです

WinMain(HINSTANCE hCurrInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	if (例外報告モードで起動された?(lpCmdLine)) {
	
		DWORD pid = コマンドラインからプロセスIDを取得(lpCmdLine);
		
		// そのプロセスが終わるまで待つ
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
		WaitForSingleObject(hProcess, INFINITE);
		CloseHandle(hProcess);
	
		// 例外を出したプロセスが終了した。
		// Windowsイベントログに反映されるまでの時間を考慮して、少し待機する
		Sleep(200);
		
		std::string event_text = イベントログを検索();
		
		テキストファイルに保存(event_text);
		
		ダイアログメッセージ(event_text);
		
		// このまま何もしないで終了
	
	} else {
		
		// 普通のゲームモードで起動された
		SetUnhandledExceptionFilter(onExceptionOccurred); // 例外フックを登録
		
		...
		// 普通のゲーム処理
		...
	
	}
	return 0;
}

で、肝心の例外フックはこんな感じ

LONG WINAPI onExceptionOccurred(struct _EXCEPTION_POINTERS *e) {

	char cmdline[256];
	コマンドライン引数作成(cmdline, GetCurrentProcessId()); // <---自分のプロセスIDも含めておく

	// 自分自身の実行ファイル名
	char self_path[MAX_PATH] = {0};
	GetModuleFileName(NULL, self_path, MAX_PATH);

	// 例外チェックモードで自分の複製を起動する
	ShellExecute(NULL, "OPEN", self_path, cmdline, NULL, SW_SHOWNORMAL);

	return EXCEPTION_CONTINUE_SEARCH; // EXCEPTION_CONTINUE_SEARCH にしないとアプリ終了時にwindowsエラーログに残らない
}

これで実験してみると、見事に不正落ちしたその場でエラーの後始末が始まります。
ようやくスマートに後始末ができるようになりました。めでたしめでたし。


サンプルを置いておきますので、興味がありましたらどうぞ。

ErrorReport_sample.zip (7.74kB)

ダウンロード

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

記事のタグから探す

月別アーカイブ

限定特典から探す

記事を検索