2011-10-25 11 views
12

Próbuję napisać system wtyczek, który może załadować zarządzane wtyczki. Host powinien mieć możliwość zwolnienia wtyczek, jeśli istnieją wyjątki. dla mojego PKOl mam bibliotekę przykładowy kod w C#, który zgłasza wyjątek jak to ...Hosting Clr i wychwytywanie wyjątków gwintowania

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

potem mam natywną aplikację konsoli win32 tak ..

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

Problem jest, że jeśli Używam powyższego kodu, host zakończyłby uruchamianie zarządzanego kodu (linia "to nigdy nie powinno drukować" zakończyłoby drukowanie) Jeśli usunę clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), to proces hosta ulegnie awarii.

Czy można zrobić cokolwiek w niezarządzanym hoście, który mógłby z wdziękiem usunąć błędną aplikację z środowiska wykonawczego i kontynuować pracę?

+0

Twój kod włączył zasady obsługi wyjątków .NET 1.x. Co właśnie kończy wątek. Nie tego, czego potrzebujesz, musisz również wywołać funkcję ICLRPolicyManager :: SetDefaultAction(), aby poinformować ją o konieczności usunięcia domeny aplikacji z wątku przerwania. Nadal masz gdzieś martwą nić, użyj __try/__ catch, aby złapać wyjątek. –

+0

Dodałem następujący wiersz clrPolicyManager-> SetDefaultAction (OPR_ThreadAbort, eUnloadAppDomain); do kodu, zaktualizowałem kod, ale efekt jest taki sam, proces hosta wciąż się zawiesza. –

+0

Być może pominięto część komentarza "martwego wątku". Musisz złapać wyjątek SEH. Kod wyjątku to 0xe0434f4d. http://msdn.microsoft.com/en-us/library/s58ftw19%28v=VS.100%29.aspx –

Odpowiedz

1

można rozpocząć nową AppDomain specjalnie dla każdej danej wtyczki i uruchomić go w środku. Zobacz http://msdn.microsoft.com/en-us/library/ms164323.aspx

Każda AppDomain to izolowane środowisko, w którym można uruchomić kod. Wyjątki występujące w jednym AppDomain mogą być izolowane od reszty. Zobacz: http://msdn.microsoft.com/en-us/library/system.appdomain(v=VS.100).aspx

+0

domeny udostępniają obszar izolowany pamięci/bezpieczeństwa, ale nie zapewniają izolacji wątków, czyli wątki są tworzone na poziomie CLR i mogą być wykonywane w dowolnej domenie, więc jeśli w wątku wystąpi nieobsługiwany wyjątek, cały CLR ulega awarii. –

+0

@ np-hard - patrz msdn: "Użyj domen aplikacji, aby wyizolować zadania, które mogą spowodować utratę procesu.Jeżeli stan AppDomain, który wykonuje zadanie staje się niestabilny, AppDomain może zostać rozładowany bez wpływu na proces. kiedy proces musi być uruchamiany przez długi czas bez ponownego uruchamiania, możesz także użyć domen aplikacji, aby wyizolować zadania, które nie powinny udostępniać danych. " (http://msdn.microsoft.com/en-us/library/system.appdomain.aspx) – Polity

+0

@ np-hard - proszę przeczytać: http://ikickandibite.blogspot.com/2010/04/appdomains-and- true-isolation.html, który dotyczy dokładnie Twojego problemu. Nie jestem pewien, czy możemy to powielić za pomocą interfejsu API CLR-Hosting. Jeśli nie, możesz stworzyć zarządzanego bootstrappera dla plugin-dll, który z wdziękiem obsługuje niewypalony wyjątek. – Polity

1

Wygląda na to, dodając następujący wraz z setDefaultAction postanawia katastrofę:

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

jak wspomniano w pytaniu "Problem polega na tym, że jeśli użyję powyższego kodu, host zakończy działanie kod zarządzany (wiersz "to nigdy nie powinno się drukować" zakończyłoby drukowanie) Jeśli usunę clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), proces hosta ulegnie awarii. " –

0

Podniosłeś bardzo interesujące pytanie, dzięki za to.

Przypuszczam, ten artykuł będzie na tyle pomocne: http://etutorials.org/Programming/programming+microsoft+visual+c+sharp+2005/Part+III+More+C+Language/Chapter+9+Exception+Handling/Unhandled+Exceptions/

+3

Nagie linki nie dają dobrych odpowiedzi. Proszę ** streść ** artykuł tutaj. Jeśli linkowana treść porusza się, ta odpowiedź staje się gorsza niż bezużyteczna. Nie musisz też podpisywać wszystkich swoich odpowiedzi, mają swój styl, który jest twoim podpisem. – ChrisF

3

Przede wszystkim, jeśli chcesz, aby zapobiec awarii aplikacji z powyższym kodzie, musisz użyć SetUnhandledExceptionFilter, tak:

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

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

Ale to może nie być to, czego naprawdę chcesz. Jednym z rozwiązań (zgodnie z propozycją Polity I) jest stworzenie pośrednika AppDomain, który może łatwo wychwycić wszystkie nieobsługiwane wyjątki. Można to zrobić w C# tak:

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

Do tego, aby móc pracować jednak trzeba zrobić dwie zmiany do swoich klas wtyczki:

  • muszą one pochodzić z MarshalByRefObject
  • metoda plugin nie muszą być statyczne (metody statyczne nazwać nie przechodzą przez filtr AppDomain)

Więc twoja klasa będzie napisane tak:

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

Jeśli to zrobisz, możesz usunąć wywołań SetUnhandledExceptionPolicy, SetActionOnFailure lub setDefaultAction, i po prostu zastąpić kod bootstrap takiego:

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

Jeśli spróbujesz to z kodu startowego powyżej, to wywołanie zwróci hr = 0x80131604, czyli COR_E_TARGETINVOCATION (TargetInvocationException).