2011-10-25 33 views
12

Tôi đang cố gắng viết một hệ thống plugin có thể tải các plugin được quản lý. Máy chủ lưu trữ sẽ có thể gỡ bỏ các plugin nếu có bất kỳ ngoại lệ nào. cho poc của tôi, tôi có một thư viện mẫu mã trong C# mà ném một ngoại lệ như thế này ...lưu trữ clr và bắt buộc luồng ngoại lệ

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

sau đó tôi có nguồn gốc ứng dụng win32 console như thế này ..

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 là nếu tôi sử dụng mã trên, máy chủ sẽ hoàn thành chạy mã được quản lý (dòng "này không bao giờ in" sẽ kết thúc in) Nếu tôi xóa clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), thì quá trình lưu trữ sẽ bị lỗi.

mọi thứ có thể được thực hiện trong máy chủ không được quản lý để có thể xóa ứng dụng không đúng cách khỏi thời gian chạy và tiếp tục làm việc?

+0

Mã của bạn đã bật chính sách xử lý ngoại lệ .NET 1.x. Mà chỉ chấm dứt sợi. Không phải những gì bạn muốn, bạn cũng cần phải gọi ICLRPolicyManager :: SetDefaultAction() để yêu cầu nó dỡ bỏ miền ứng dụng trên một chuỗi bị hủy bỏ. Bạn vẫn còn một thread chết ở đâu đó, sử dụng __try/__ catch để bắt ngoại lệ. –

+0

Tôi đã thêm dòng sau clrPolicyManager-> SetDefaultAction (OPR_ThreadAbort, eUnloadAppDomain); để mã, tôi đã cập nhật mã, nhưng hiệu quả là như nhau, quá trình lưu trữ vẫn treo –

+0

Bạn có thể đã bỏ lỡ phần "chuỗi chết" của nhận xét. Bạn phải bắt ngoại lệ SEH. Mã ngoại lệ là 0xe0434f4d. http://msdn.microsoft.com/en-us/library/s58ftw19%28v=VS.100%29.aspx –

Trả lời

1

Bạn có thể bắt đầu một AppDomain mới đặc biệt cho mỗi plugin nhất định và khởi động nó bên trong. Xem http://msdn.microsoft.com/en-us/library/ms164323.aspx

Mỗi AppDomain là một môi trường riêng biệt nơi mã có thể thực thi. Các ngoại lệ xảy ra trong một AppDomain có thể được phân lập từ phần còn lại. Xem: http://msdn.microsoft.com/en-us/library/system.appdomain(v=VS.100).aspx

+0

tên miền cung cấp bộ nhớ bảo mật/an toàn, nhưng chúng không cung cấp cách ly chuỗi, đó là các chủ đề được tạo ở cấp CLR và chúng có thể thực thi trong bất kỳ miền nào, vì vậy nếu ngoại lệ không được giải quyết xảy ra trên một chuỗi, toàn bộ CLR bị treo. –

+0

@ np-hard - xem msdn: "Sử dụng các miền ứng dụng để tách biệt các tác vụ có thể làm giảm quy trình. Nếu trạng thái của AppDomain thực thi tác vụ trở nên không ổn định, AppDomain có thể được tải xuống mà không ảnh hưởng đến quá trình. khi một quá trình phải chạy trong thời gian dài mà không cần khởi động lại. Bạn cũng có thể sử dụng các miền ứng dụng để tách biệt các tác vụ không nên chia sẻ dữ liệu. " (http://msdn.microsoft.com/en-us/library/system.appdomain.aspx) – Polity

+0

@ np-hard - Vui lòng đọc: http://ikickandibite.blogspot.com/2010/04/appdomains-and- true-isolation.html đề cập đến chính xác vấn đề của bạn. Tôi không chắc liệu chúng ta có thể tái tạo điều này bằng cách sử dụng API CLR-Hosting hay không. Nếu không, bạn có thể phát triển một bootstrapper được quản lý cho plugin-dll xử lý một cách duyên dáng ngoại lệ không được chiếu sáng – Polity

1

Trông giống như thêm sau cùng với SetDefaultAction giải quyết các vụ tai nạn:

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

như đã đề cập trong câu hỏi "Vấn đề là nếu tôi sử dụng mã trên, máy chủ sẽ hoàn tất chạy mã được quản lý (dòng "điều này sẽ không bao giờ in" sẽ kết thúc in) Nếu tôi xóa clrPolicyManager-> SetUnhandledExceptionPolicy (eHostDeterminedPolicy), thì quá trình lưu trữ sẽ bị lỗi ". –

0

Bạn đã đưa ra một câu hỏi rất thú vị, cảm ơn vì điều đó.

Tôi cho rằng bài viết này sẽ là đủ hữu ích: http://etutorials.org/Programming/programming+microsoft+visual+c+sharp+2005/Part+III+More+C+Language/Chapter+9+Exception+Handling/Unhandled+Exceptions/

+3

Liên kết giả mạo không đưa ra câu trả lời hay. Xin vui lòng bạn có thể ** tóm tắt ** bài viết ở đây. Nếu nội dung được liên kết bao giờ chuyển câu trả lời này trở nên tệ hơn vô dụng. Ngoài ra không cần phải ký tất cả các câu trả lời của bạn, họ có sự tinh tế của bạn kèm theo đó là chữ ký của bạn. – ChrisF

3

Trước hết, nếu bạn muốn ngăn chặn tai nạn ứng dụng với mã trên, bạn sẽ cần phải sử dụng SetUnhandledExceptionFilter, như thế này:

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

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

Nhưng điều này có thể không phải là những gì bạn thực sự muốn. Một giải pháp (như được đề xuất bởi Polity tôi tin) là tạo ra một AppDomain trung gian có thể dễ dàng nắm bắt tất cả các ngoại lệ chưa được giải quyết. Bạn có thể làm điều đó trong C#, như thế này:

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

Đối với điều này để có thể làm việc tuy nhiên, bạn cần phải làm hai thay đổi đến các lớp học plugin của bạn:

  • họ phải xuất phát từ MarshalByRefObject
  • phương pháp Plugin không phải tĩnh (phương pháp tĩnh gọi không đi qua bộ lọc AppDomain)

Vì vậy, lớp học của bạn sẽ được viết như thế này:

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

Nếu bạn làm điều này, bạn có thể loại bỏ các cuộc gọi đến SetUnhandledExceptionPolicy, SetActionOnFailure, hoặc SetDefaultAction, và chỉ cần thay thế mã bootstrap như thế này:

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

Nếu bạn cố gắng này với mã Startup bạn ở trên, cuộc gọi này sẽ trả về hr = 0x80131604, là COR_E_TARGETINVOCATION (TargetInvocationException).

Các vấn đề liên quan