2011-10-25 6 views
12

J'essaye d'écrire un système de plugin qui peut charger des plugins gérés. L'hôte devrait pouvoir décharger les plugins s'il y a des exceptions. pour mon poc J'ai une bibliothèque de code exemple en C# qui jette une exception comme ça ...clr d'hébergement et attraper des exceptions de threading

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

alors j'ai console app native win32 comme ça ..

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

problème est que si J'utilise le code ci-dessus, l'hôte terminerait l'exécution du code managé (la ligne "ceci ne devrait jamais s'imprimer" finirait par imprimer) Si je supprime le clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), le processus hôte se bloquerait.

peut-il être fait quelque chose dans l'hôte non géré qu'il pourrait gracieusement supprimer l'application errant de l'exécution et continuer à travailler?

+0

Votre code a activé la règle de gestion des exceptions .NET 1.x. Qui vient de terminer le fil. Pas ce que vous voulez, vous devrez également appeler ICLRPolicyManager :: SetDefaultAction() pour lui dire de décharger le domaine de l'application sur un abandon de thread. Vous avez toujours un thread mort quelque part, utilisez __try/__ catch pour attraper l'exception. –

+0

J'ai ajouté la ligne suivante clrPolicyManager-> SetDefaultAction (OPR_ThreadAbort, eUnloadAppDomain); pour le code, j'ai mis à jour le code, mais l'effet est le même, le processus hôte se bloque toujours –

+0

Vous avez peut-être manqué la partie "thread mort" du commentaire. Vous devez attraper l'exception SEH. Le code d'exception est 0xe0434f4d. http://msdn.microsoft.com/en-us/library/s58ftw19%28v=VS.100%29.aspx –

Répondre

1

Vous pouvez commencer une nouvelle AppDomain spécifiquement pour chaque plug-in donné et le lancer à l'intérieur. Voir http://msdn.microsoft.com/en-us/library/ms164323.aspx

Chaque domaine d'application est un environnement isolé où le code peut s'exécuter. Les exceptions se produisant dans un AppDomain peuvent être isolées du reste. Voir: http://msdn.microsoft.com/en-us/library/system.appdomain(v=VS.100).aspx

+0

fournissent un sandbox de sécurité/mémoire, mais ils ne fournissent pas d'isolation de thread, ce qui signifie que les threads sont créés au niveau CLR et peuvent s'exécuter dans n'importe quel domaine. –

+0

@ np-hard - see msdn: «Utilisez les domaines d'application pour isoler les tâches susceptibles d'abattre un processus Si l'état de l'AppDomain qui exécute une tâche devient instable, AppDomain peut être déchargé sans affecter le processus. Lorsqu'un processus doit être exécuté pendant de longues périodes sans redémarrage, vous pouvez également utiliser des domaines d'application pour isoler les tâches qui ne doivent pas partager de données. " (http://msdn.microsoft.com/en-us/library/system.appdomain.aspx) – Polity

+0

@ np-hard - Veuillez lire: http://ikickandibite.blogspot.com/2010/04/appdomains-and- true-isolation.html qui traite exactement de votre problème. Je ne suis pas sûr si nous pouvons répliquer cela en utilisant l'API CLR-Hosting. Si ce n'est pas le cas, vous pouvez développer un bootstrappeur managé pour le plugin-dll qui gère avec élégance une exception non gérée – Polity

1

On dirait que l'ajout suivant avec setDefaultAction résout le crash:

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

comme mentionné dans la question "Problème est que si j'utilise le code ci-dessus, l'hôte terminerait l'exécution de la code géré (la ligne "cela ne devrait jamais imprimer" finirait par imprimer) Si je supprime le clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), alors le processus hôte se bloquerait. " Les domaines –

0

Vous avez soulevé une question très intéressante, merci pour cela.

Je suppose que cet article sera utile assez: http://etutorials.org/Programming/programming+microsoft+visual+c+sharp+2005/Part+III+More+C+Language/Chapter+9+Exception+Handling/Unhandled+Exceptions/

+3

Les liens nus ne font pas de bonnes réponses. S'il vous plaît pouvez-vous ** résumer ** l'article ici. Si le contenu lié bouge, cette réponse devient pire qu'inutile. De plus, il n'est pas nécessaire de signer toutes vos réponses, ils ont votre flair attaché qui est votre signature. – ChrisF

3

Tout d'abord, si vous voulez éviter plantage de l'application avec le code ci-dessus, vous aurez besoin d'utiliser SetUnhandledExceptionFilter, comme ceci:

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

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

Mais ce n'est peut-être pas ce que vous voulez vraiment. Une solution (telle que proposée par Polity je crois) est de créer un AppDomain intermédiaire qui peut facilement capturer toutes les exceptions non gérées. Vous pouvez le faire en C#, comme ceci:

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

Pour que cela soit en mesure de travailler cependant, vous devez faire deux changements à vos classes de plug-ins:

  • ils doivent tirer de MarshalByRefObject
  • la méthode de plug-in ne doit pas être statique (méthodes statiques appel ne passent pas par le filtre AppDomain)

Ainsi, votre classe serait écrit comme ceci:

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

Si vous faites cela, vous pouvez supprimer les appels à SetUnhandledExceptionPolicy, SetActionOnFailure ou setDefaultAction, et il suffit de remplacer le code d'amorçage comme ceci:

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

Si vous essayez avec votre code de démarrage ci-dessus, cet appel renverra hr = 0x80131604, COR_E_TARGETINVOCATION (TargetInvocationException).