I am trying to write an plugin system that can load managed plugins. The host should be able to unload the plugins if there are any exceptions. for my poc I have a sample code library in C# that throws an exception like this ...
public static int StartUp(string arguments)
{
Console.WriteLine("Started exception thrower with args {0}", arguments);
Thread workerThread = new Thread(() =>
{
Console.WriteLine("Starting a thread, doing some important work");
Thread.Sleep(1000);
throw new ApplicationException();
}
);
workerThread.Start();
workerThread.Join();
Console.WriteLine("this should never print");
return 11;
}
then i have native win32 console app like this ..
int _tmain(int argc, _TCHAR* argv[])
{
ICLRMetaHost *pMetaHost = NULL;
HRESULT hr;
ICLRRuntimeInfo *runtimeInfo = NULL;
__try
{
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
hr = pMetaHost->GetRuntime(L"v4.0.30319",IID_ICLRRuntimeInfo,(LPVOID*)&runtimeInfo);
ICLRRuntimeHost *runtimeHost = NULL;
hr = runtimeInfo->GetInterface(CLSID_CLRRuntimeHost,IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost);
ICLRControl* clrControl = NULL;
hr = runtimeHost->GetCLRControl(&clrControl);
ICLRPolicyManager *clrPolicyManager = NULL;
clrControl->GetCLRManager(IID_ICLRPolicyManager, (LPVOID*)&clrPolicyManager);
clrPolicyManager->SetDefaultAction(OPR_ThreadAbort,eUnloadAppDomain);
hr = runtimeHost->Start();
DWORD returnVal = NULL;
hr = runtimeHost->ExecuteInDefaultAppDomain(L"ExceptionThrower.dll",L"ExceptionThrower.MainExceptionThrower",L"StartUp",L"test",&returnVal);
runtimeHost->Release();
}
__except(1)
{
wprintf(L"\n Error thrown %d",e);
}
return 0;
}
Issue is that if i use the above code, the host would complete running the managed code (the line "this should never print" would end up printing) If i remove the clrPolicyManager->SetUnhandledExceptionPolicy(eHostDeterminedPolicy), then the host process would crash.
can anything be done in the unmanaged host that it could gracefully remove the errant app from runtime and continue working ?
Looks like adding following together with SetDefaultAction resolves the crash:
First of all, if you want to prevent application crash with the code above, you'll need to use SetUnhandledExceptionFilter, like this:
But this may not be what you really want. One solution (as proposed by Polity I believe) is to create an intermediary AppDomain that can catch easily all unhandled exceptions. You can do that in C#, like this:
For this to be able to work however, you need to do two changes to your plugin classes:
So your class would be written like this:
If you do this, you can remove the calls to SetUnhandledExceptionPolicy, SetActionOnFailure, or SetDefaultAction, and just replace the bootstrap code like this:
If you try this with your Startup code above, this call will return hr=0x80131604, which is COR_E_TARGETINVOCATION (TargetInvocationException).
You brought up a very interesting question, thanks for that.
I suppose this article will be helpful enough: http://etutorials.org/Programming/programming+microsoft+visual+c+sharp+2005/Part+III+More+C+Language/Chapter+9+Exception+Handling/Unhandled+Exceptions/
You can start a new AppDomain specifically for each given plugin and launch it inside. See http://msdn.microsoft.com/en-us/library/ms164323.aspx
Each AppDomain is an isolated environment where code can execute. Exceptions occuring in one AppDomain can be isolated from th rest. See: http://msdn.microsoft.com/en-us/library/system.appdomain(v=VS.100).aspx