2011-10-25 17 views
12

Estoy tratando de escribir un sistema de complemento que pueda cargar complementos administrados. El host debería poder descargar los complementos si hay alguna excepción. para mi poc Tengo una biblioteca de código de ejemplo en C# que se produce una excepción como esta ...hosting clr y catching threading exceptions

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; 
    } 

entonces he aplicación de consola Win32 nativa como esto ..

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; 
} 

tema es que si utilizo el código anterior, el host completaría la ejecución del código administrado (la línea "esto nunca debería imprimir" terminaría imprimiendo) Si elimino clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), el proceso de host se bloqueará.

¿Se puede hacer algo en el host no administrado que podría eliminar con gracia la aplicación errante del tiempo de ejecución y continuar funcionando?

+0

Su código habilitó la política de manejo de excepciones de .NET 1.x. Lo cual solo termina el hilo. No es lo que quiere, también necesitará llamar a ICLRPolicyManager :: SetDefaultAction() para indicarle que descargue el dominio de la aplicación en un aborto de subprocesos. Todavía tiene un hilo muerto en alguna parte, use __try/__ catch para capturar la excepción. –

+0

Agregué la siguiente línea clrPolicyManager-> SetDefaultAction (OPR_ThreadAbort, eUnloadAppDomain); para el código, he actualizado el código, pero el efecto es el mismo, el proceso del host todavía se cuelga. –

+0

Es posible que haya perdido la parte del comentario "dead thread". Debes atrapar la excepción SEH. El código de excepción es 0xe0434f4d. http://msdn.microsoft.com/en-us/library/s58ftw19%28v=VS.100%29.aspx –

Respuesta

1

Puede iniciar un nuevo dominio de aplicación específicamente para cada plugin dado y lanzarlo dentro. Ver http://msdn.microsoft.com/en-us/library/ms164323.aspx

Cada AppDomain es un entorno aislado donde se puede ejecutar el código. Las excepciones que ocurren en un AppDomain se pueden aislar del resto. Ver: http://msdn.microsoft.com/en-us/library/system.appdomain(v=VS.100).aspx

+0

proporcionan un entorno limitado de seguridad/memoria, pero no proporcionan aislamiento de subprocesos, es decir, los subprocesos se crean a nivel CLR y se pueden ejecutar en cualquier dominio, por lo que si ocurre una excepción no controlada en un subproceso, todo CLR se bloquea. –

+0

@ np-hard - ver msdn: "Usar dominios de aplicación para aislar tareas que puedan hacer que un proceso disminuya. Si el estado del AppDomain que está ejecutando una tarea se vuelve inestable, el AppDomain se puede descargar sin afectar el proceso. Esto es importante cuando un proceso debe ejecutarse durante largos períodos sin reiniciar. También puede usar dominios de aplicación para aislar tareas que no deben compartir datos ". (http://msdn.microsoft.com/en-us/library/system.appdomain.aspx) – Polity

+0

@ np-hard - Lea: http://ikickandibite.blogspot.com/2010/04/appdomains-and- true-isolation.html que trata exactamente con su problema. No estoy seguro si podemos replicar esto usando la API CLR-Hosting. De lo contrario, puede desarrollar un bootstrapper administrado para plugin-dll que gestione con gracia una excepción no modificada – Polity

1

Parece que la adición siguiente junto con setDefaultAction resuelve el accidente:

clrPolicyManager->SetUnhandledExceptionPolicy(EClrUnhandledException::eHostDeterminedPolicy); 
+0

como se menciona en la pregunta "El problema es que si uso el código anterior, el host completará la ejecución del código administrado (la línea "esto nunca debería imprimirse" terminaría imprimiendo) Si elimino clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), entonces el proceso de host se bloqueará ". Los dominios –

0

Has planteado una pregunta muy interesante, gracias por eso.

supongo que este artículo va a ser lo suficientemente útil: http://etutorials.org/Programming/programming+microsoft+visual+c+sharp+2005/Part+III+More+C+Language/Chapter+9+Exception+Handling/Unhandled+Exceptions/

+3

Los enlaces desnudos no ofrecen buenas respuestas. Por favor, ¿puedes ** resumir ** el artículo aquí? Si el contenido vinculado alguna vez se mueve, esta respuesta se vuelve peor que inútil. Además, no hay necesidad de firmar todas sus respuestas, tienen su estilo adjunto, que es su firma. – ChrisF

3

En primer lugar, si se quiere evitar que la aplicación de choque con el código anterior, tendrá que utilizar SetUnhandledExceptionFilter, así:

LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionInfo) 
{ 
    // do something useful 
    return EXCEPTION_EXECUTE_HANDLER; // prevent crash 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); 
     ... 
} 

Pero esto puede no ser lo que realmente quieres. Una solución (como lo propone Polity, creo) es crear un AppDomain intermediario que pueda detectar fácilmente todas las excepciones no controladas. Usted puede hacer eso en C#, así:

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); 
    } 
} 

Para que esto sea capaz de trabajar sin embargo, que hay que hacer dos cambios en sus clases de plugin:

  • deben derivar de MarshalByRefObject
  • el método plugin no debe ser estática (métodos estáticos llaman que no pasan por el filtro dominio de aplicación)

por lo que su clase se escribiría así:

public class MainExceptionThrower: MarshalByRefObject 
{ 
    public int StartUp(string arguments) 
    { 
    ... 
    } 
} 

Si hace esto, puede eliminar las llamadas a SetUnhandledExceptionPolicy, SetActionOnFailure o setDefaultAction, y simplemente reemplazar el código de arranque de esta manera:

hr = runtimeHost->ExecuteInDefaultAppDomain(L"PluginSystem.dll", L"PluginSystem.PluginVerifier", L"CheckPlugin", L"test", &returnVal);   

Si intenta esto con su código de inicio anterior, esta llamada devolverá hr = 0x80131604, que es COR_E_TARGETINVOCATION (TargetInvocationException).