例
int *ptr;
*ptr = 1000;
Microsoft固有のものを使用せずに、標準のC ++を使用してメモリアクセス違反の例外をキャッチできますか。
回答:
いいえ。C ++は、何か悪いことをしたときに例外をスローしません。これにより、パフォーマンスが低下します。アクセス違反やゼロ除算エラーのようなものは、キャッチできる言語レベルのものというよりも、「マシン」例外のようなものです。
それを読んで泣いてください!
私はそれを考え出した。ハンドラーからスローしない場合、ハンドラーは続行するだけで、例外も続行します。
あなたがあなた自身の例外を投げてそれを処理するとき、魔法は起こります。
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>
void SignalHandler(int signal)
{
printf("Signal %d",signal);
throw "!Access Violation!";
}
int main()
{
typedef void (*SignalHandlerPointer)(int);
SignalHandlerPointer previousHandler;
previousHandler = signal(SIGSEGV , SignalHandler);
try{
*(int *) 0 = 0;// Baaaaaaad thing that should never be caught. You should write good code in the first place.
}
catch(char *e)
{
printf("Exception Caught: %s\n",e);
}
printf("Now we continue, unhindered, like the abomination never happened. (I am an EVIL genius)\n");
printf("But please kids, DONT TRY THIS AT HOME ;)\n");
}
sigaltstack
)がインストールされていない必要があり(C ++例外アンワインド実装で許可されている場合を除く)、アンワインドメカニズム自体を処理するすべてのランタイム関数はシグナルセーフである必要があります。
signal(SIGSEGV, SIG_DFL);
try- > catch(...)ブロックを使用して、Visual Studioであらゆる種類の例外(ゼロ除算、アクセス違反など)をキャッチする非常に簡単な方法があります。マイナーなプロジェクト設定の調整で十分です。プロジェクト設定で/ EHaオプションを有効にするだけです。「プロジェクトのプロパティ」->「C / C ++」->「コード生成」->「C ++例外を有効にする」を「SEH例外ではい」に変更するを参照してください。それでおしまい!
詳細はこちらをご覧ください:http: //msdn.microsoft.com/en-us/library/1deeycx5(v = vs.80).aspx
少なくとも私にとっては、signal(SIGSEGV ...)
別の回答で言及されているアプローチは、Visual C ++ 2015を使用するWin32では機能しませんでした。私にとってうまくいったのは、にあるを使用する_set_se_translator()
ことeh.h
でした。それはこのように動作します:
ステップ1)Volodymyr Frytskyyの回答に記載されているように、プロジェクトプロパティ/ C ++ /コード生成/ C ++例外の有効化でSEH例外(/ EHa)を使用して[はい]を有効にしていることを確認します。
ステップ2)を呼び出し_set_se_translator()
、新しい例外トランスレータの関数ポインタ(またはラムダ)を渡します。これは、基本的に低レベルの例外を取得し、次のようにキャッチしやすいものとして再スローするため、トランスレータと呼ばれstd::exception
ます。
#include <string>
#include <eh.h>
// Be sure to enable "Yes with SEH Exceptions (/EHa)" in C++ / Code Generation;
_set_se_translator([](unsigned int u, EXCEPTION_POINTERS *pExp) {
std::string error = "SE Exception: ";
switch (u) {
case 0xC0000005:
error += "Access Violation";
break;
default:
char result[11];
sprintf_s(result, 11, "0x%08X", u);
error += result;
};
throw std::exception(error.c_str());
});
ステップ3)通常どおりに例外をキャッチします。
try{
MakeAnException();
}
catch(std::exception ex){
HandleIt();
};
このタイプの状況は実装に依存するため、トラップするにはベンダー固有のメカニズムが必要になります。Microsoftの場合、これにはSEHが含まれ、* nixにはシグナルが含まれます。
一般に、アクセス違反の例外をキャッチすることは非常に悪い考えです。AV例外から回復する方法はほとんどありません。回復しようとすると、プログラムのバグを見つけるのが難しくなります。
述べたように、Windowsプラットフォームでこれを行うMicrosoft /コンパイラベンダー以外の方法はありません。ただし、これらのタイプの例外を通常のtry {} catch(exception ex){}の方法でキャッチして、エラーを報告し、アプリをより適切に終了することは明らかに便利です(JaredParが言うように、アプリはおそらく問題を抱えています) 。単純なクラスラッパーで_se_translator_functionを使用して、tryハンドラーで次の例外をキャッチできるようにします。
DECLARE_EXCEPTION_CLASS(datatype_misalignment)
DECLARE_EXCEPTION_CLASS(breakpoint)
DECLARE_EXCEPTION_CLASS(single_step)
DECLARE_EXCEPTION_CLASS(array_bounds_exceeded)
DECLARE_EXCEPTION_CLASS(flt_denormal_operand)
DECLARE_EXCEPTION_CLASS(flt_divide_by_zero)
DECLARE_EXCEPTION_CLASS(flt_inexact_result)
DECLARE_EXCEPTION_CLASS(flt_invalid_operation)
DECLARE_EXCEPTION_CLASS(flt_overflow)
DECLARE_EXCEPTION_CLASS(flt_stack_check)
DECLARE_EXCEPTION_CLASS(flt_underflow)
DECLARE_EXCEPTION_CLASS(int_divide_by_zero)
DECLARE_EXCEPTION_CLASS(int_overflow)
DECLARE_EXCEPTION_CLASS(priv_instruction)
DECLARE_EXCEPTION_CLASS(in_page_error)
DECLARE_EXCEPTION_CLASS(illegal_instruction)
DECLARE_EXCEPTION_CLASS(noncontinuable_exception)
DECLARE_EXCEPTION_CLASS(stack_overflow)
DECLARE_EXCEPTION_CLASS(invalid_disposition)
DECLARE_EXCEPTION_CLASS(guard_page)
DECLARE_EXCEPTION_CLASS(invalid_handle)
DECLARE_EXCEPTION_CLASS(microsoft_cpp)
元のクラスは、この非常に役立つ記事からのものです。
例外処理メカニズムではありませんが、Cによって提供されるsignal()メカニズムを使用できます。
> man signal
11 SIGSEGV create core image segmentation violation
NULLポインターへの書き込みは、おそらくSIGSEGVシグナルを引き起こします。
signal()
はposix標準の一部です。Windowsは、POSIX標準(LinuxおよびUNIXがするように)を実装
そのような違反は、コードに重大な問題があり、信頼性が低いことを意味します。ユーザーのデータがまだ破損していないことを期待して、プログラムが以前のデータを上書きしないことを期待する方法でユーザーのデータを保存しようとする可能性があることがわかりますが、定義上、標準的な方法はありません未定義の振る舞いに対処すること。