こんにちわ、ランタイムのアサートが嫌いな「ふじひー」です。
プログラミングしている時に、実行したアプリを勝手に終了されたくないですよね~。
アクセス違反もそうです。しっかりヌルチェックしてアプリを勝手に終了しないようにしましょう!
でもうっかりヌルチェック漏れがあって、アクセス違反起こしたりしちゃいますよね。
とくにUnityとかでホットリロードしまくっていると、ついヌルチェック漏れでログにアクセス違反のエラーが大量に出されるみたいな事ありますよね。
でもUnityはエンジンが終了しないですよね?
よしそれをC++で作ろう!
と言う事で、
「安易に真似しない方が良い、ゲーム開発にそんなに役に立たないプログラミング講座 第二弾」します。
今回はタイトルにもある通り、アクセス違反を検知して処理しアプリを勝手に終了しないようにします。
それでは「構造化例外」でググって下さい。はい、完成です。
が、それでは読者さんは納得行かないと思うので作りました。
↓こちらが完成した物になります。
っとその前に、利用する際はプロジェクトのプロパティ変更が必要です。
プロジェクトのプロパティを開いて、
[構成プロパティ]>[C/C++]>[コード生成]>[C++ の例外を有効にする]を
[はい(/EHsc)]から[はい – SEHの例外あり(/EHa)]に変えます。
これで準備OKです。
コードの紹介
まず関数の呼び出し順番が分かりやすように、PrintScope構造体を作りました。
検証用に構造化例外を発生させる関数を用意。
実装。
実行結果
Start Main
Start Try
Start Exec
Start AccessViolation
**デバッグ実行ならここで例外スローのダイアログ**
Start SE_TransFunc
End SE_TransFunc
End AccessViolation
End Exec
End Try
Start Catch
End Catch
End Main
”おわり”が出力されずに、処理を抜けているのがわかります。
構造化例外時のログ出力を強化しましょう。
実行結果
Start Main
Start Try
Start Exec
Start AccessViolation
Start SE_TransFunc
*——
[Exception ] アクセス違反
[File Name ] c:\users\UserName\desktop\my\seh\seh_test\source\main.cpp(180)
[Call Stack] ExceptionFactory::AccessViolation
[Call Stack] main
[Call Stack] invoke_main
[Call Stack] __scrt_common_main_seh
[Call Stack] __scrt_common_main
[Call Stack] mainCRTStartup
[Call Stack] BaseThreadInitThunk
[Call Stack] RtlInitializeExceptionChain
[Call Stack] RtlInitializeExceptionChain
*——
End SE_TransFunc
End AccessViolation
End Exec
End Try
Start Catch
End Catch
End Main
コールスタックと例外発生した位置が分かるようになりました!(今回のタイトルと関係なし!)
全ソースコード(長いのでリンクのみ)
https://gist.github.com/hexagit/fbe0d86b9209c154caa0a1b062347aed
って例外を処理してないから、結局アクセス違反する問題は解決できてないけど、そこは上手くやって下さい。
あとは、最適化でインライン展開するとコールスタックが分かりづらかったり。
スタックオーバーフローの例外だと、ログ出力の為の関数とか呼び出したタイミングでエラーでるので、改良して下さい。
余談
実行中のアプリで、バグを発見したり、アクセス違反で止まったりした時に、修正して再起動って面倒ですよね。
Visual Studioで変数をクリックすれば実行中でも変数の中身書き換えたり、
実行中にコードに変更を加えて[デバッグ]>[コードの変更を適用]で再起動することなく編集したり、
実行中にブレイクポイントなりで停止させて、好きな行で[右クリック]>[次のステートメントの設定]で、エラー出ている所をスキップしたり、IF分の中に無理やり入れたりできる。
これで君もアプリを勝手に終了させないマスターだ(?)
ふじひーの前回のブログ(ホットリロードの解説とか)