首先,如果您想使用上面的代码防止应用程序崩溃,您需要使用设置未处理异常过滤器 http://msdn.microsoft.com/en-us/library/windows/desktop/ms680634%28v=vs.85%29.aspx, 像这样:
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionInfo)
{
// do something useful
return EXCEPTION_EXECUTE_HANDLER; // prevent crash
}
int _tmain(int argc, _TCHAR* argv[])
{
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
...
}
但这可能不是您真正想要的。一种解决方案(我相信由 Poity 提出)是创建一个中间 AppDomain,它可以轻松捕获所有未处理的异常。您可以在 C# 中执行此操作,如下所示:
public class PluginVerifier
{
public static int CheckPlugin(string arguments)
{
AppDomain appDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());
appDomain.UnhandledException += AppDomainUnhandledException;
object obj = appDomain.CreateInstanceAndUnwrap("ExceptionThrower", "ExceptionThrower.MainExceptionThrower");
object ret = obj.GetType().InvokeMember("Startup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, obj, new object[] { arguments });
AppDomain.Unload(appDomain);
return (int)ret;
}
private static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
AppDomain appDomain = (AppDomain)sender;
// the following will prevent "this should never print" to happen
AppDomain.Unload(appDomain);
}
}
然而,为了使其能够工作,您需要对插件类进行两处更改:
- 它们必须从 MarshalByRefObject 派生
- 插件方法不能是静态的(静态方法调用不经过AppDomain过滤器)
所以你的类应该这样写:
public class MainExceptionThrower: MarshalByRefObject
{
public int StartUp(string arguments)
{
...
}
}
如果这样做,您可以删除对 SetUnhandledExceptionPolicy、SetActionOnFailure 或 SetDefaultAction 的调用,而只需替换引导代码,如下所示:
hr = runtimeHost->ExecuteInDefaultAppDomain(L"PluginSystem.dll", L"PluginSystem.PluginVerifier", L"CheckPlugin", L"test", &returnVal);
如果您使用上面的启动代码尝试此操作,则此调用将返回 hr=0x80131604,即 COR_E_TARGETINVOCATION (TargetInitationException)。