简体   繁体   English

如何正确处理系统异常?

[英]How do I correctly handle system exceptions?

I'm trouble with the exception handling. 我在处理异常方面很麻烦。 Specifically, I create a System.Diagnostic.Process object from the process identifier (PID), then I use it to execute my algorithm. 具体来说,我从进程标识符(PID)创建一个System.Diagnostic.Process对象,然后使用它执行我的算法。 I've noticed that this class throw InvalidOperation and ArgumentException exception when accessing to different properties, because the process has already exited while I'm accessing to the Process instance. 我注意到该类在访问不同的属性时会引发InvalidOperation和ArgumentException异常,因为在我访问Process实例时该过程已经退出。

However, the algorithm uses other function which throw the same exceptions. 但是,该算法使用其他引发相同异常的函数。 The following code is the one which has raised the problem: 以下代码是引发此问题的代码:

XmlWindow mWindow = new XmlWindow(new IntPtr(args.Message.GetContent<Int64>()));
Process mWindowProcess = mWindow.GetProcess();
XmlProcessInstance mWindowProcessInstance = null;
XmlProcessLayout pLayout = null;

Log(mWindow);

lock (mCoreData.ProcessList) {
    try {
        // Ensure matching XmlProcess
        mCoreData.ProcessList.EnsureProcessManagement(mWindowProcess);
    } catch (System.ComponentModel.Win32Exception eWin32Exception) {
        sLog.WarnFormat("Unable to manage window creation ({0}, error code {1}).", eWin32Exception.Message, eWin32Exception.NativeErrorCode);
        break;
    }
}

lock (mCoreData.LayoutList) {
    // Unmanaged process?
    if (mCoreData.LayoutList.IsManagedProcessInstance(mWindowProcess) == false) {
        lock (mCoreData.UnmanagedLayout) {
            // Force process management
            if ((mWindowProcessInstance = mCoreData.UnmanagedLayout.GetProcessInstance((uint)mWindowProcess.Id)) == null) {
                mWindowProcessInstance = mCoreData.UnmanagedLayout.ManageProcessInstance((uint)mWindowProcess.Id, mCoreData.ProcessList);
                sLog.DebugFormat("Layout \"{0}\" will manage explictly the process \"{1}\" ({2}).", mCoreData.UnmanagedLayout.Name, mWindowProcessInstance.ApplicationName, mWindowProcessInstance.InstanceId);
            }
        }
    } else {
        // Find the (managed) process instance
        mWindowProcessInstance = mCoreData.LayoutList.GetProcessInstance((uint)mWindowProcess.Id);
    }
}

Log(mWindowProcessInstance);

// Ensure window match
mWindowProcessInstance.ProcessAssociation.AssociatedItem.LearnWindowMatching(mWindow);
// Register process instance window
mWindowProcessInstance.LearnWindowTemplating(mWindow);
mWindowProcessInstance.Windows.Add(mWindow);
// Apply window template (if any)
mWindowProcessInstance.ApplyTemplateWindow(mWindow);

The problem is how to manage the InvalidOperationException exception. 问题是如何管理InvalidOperationException异常。 The code above doesn't work, since the exception could be thrown by SomeFunction , instead by accessing the Process instance; 上面的代码不起作用,因为可以通过SomeFunction抛出异常,而不是通过访问Process实例抛出该异常; I need to handle only those exception thrown by the mWindowProcess . 我只需要处理mWindowProcess抛出的那些异常。

Of course I need a big one try/catch statement, because the usage of the variable mWindowProcess is very intensive 当然,我需要一条很大的try / catch语句,因为变量mWindowProcess的使用非常繁琐

How this could be solved correctly? 如何正确解决呢?

You can use two try-catch blocks. 您可以使用两个try-catch块。

Process p = Process.GetProcessById(pid);
try {
    SomeFunction(); // could throw InvalidOperationException
} catch (InvalidOperationException) {  } catch { throw; }
try {
    if (p.Id == 1234) { ... } // could throw InvalidOPerationException!
} catch (InvalidOperationException) {  } catch { throw; }

Use two seperate try/catch blocks. 使用两个单独的try / catch块。 Each block handles the same exception differently. 每个块对相同异常的处理方式都不同。

You could check Process.HasExited before each call and determine what to do if the process has exited at that point. 您可以在每次调用之前检查Process.HasExited ,并确定如果此时已退出该怎么办。 It's unclear whether there is a systematic way to handle this for your application. 尚不清楚是否有系统的方法可以为您的应用程序处理此问题。 Unfortunately you still need to check for the exception, since the process could terminate between the query call and the usage of the Process class. 不幸的是,您仍需要检查异常,因为该过程可能在查询调用和Process类的使用之间终止。 It's unfortunate that InvalidOperationException was used, since this is often used to indicate unrecoverable corrupted state. 不幸的是,使用了InvalidOperationException,因为它通常用于指示不可恢复的损坏状态。

The correct way to do this is, unfortunately, to put a try catch around every specific call for which you wish to handle the error. 不幸的是,执行此操作的正确方法是尝试捕获要处理错误的每个特定调用。 If you want to exit the larger usage block, you could then throw your own custom exception that's more indicative of the true failure (ProcessTerminatedException, for instance.) One option to clean this up: 如果要退出较大的使用量块,则可以引发自己的自定义异常,该异常更能指示真正的失败(例如,ProcessTerminatedException。)一种清除方法:

    public static int SafeGetId(this Process process)
    {
        if (process == null) throw new ArgumentNullException("process");

        try
        {
            return process.Id;
        }
        catch (InvalidOperationException ex)
        {
            //Do special logic, such as wrap in a custom ProcessTerminatedException
            throw;
        }
    }

Now you can call SafeGetId() everywhere you were previously accessing Id. 现在,您可以在以前访问ID的任何地方调用SafeGetId()。 You can create additional wrappers for other methods/properties that could fail. 您可以为其他可能失败的方法/属性创建其他包装。

I found a possible answer. 我找到了可能的答案。 Effectively this solution was unexpected as much as obvious... 实际上,这个解决方案出乎意料的是……

This is quote of the Exception documentation: 这是异常文档的报价:

Exception includes a number of properties that help identify the code location, the type, the help file, and the reason for the exception: StackTrace, InnerException, Message, HelpLink, HResult, Source, TargetSite, and Data. 异常包括许多有助于识别代码位置,类型,帮助文件和异常原因的属性:StackTrace,InnerException,Message,HelpLink,HResult,Source,TargetSite和Data。

The listed Exception properties really aid the exception catching. 列出的Exception属性确实有助于捕获异常。 In my case, it is acceptable to catch only those exception thrown by the Process class. 就我而言,仅捕获Process类抛出的那些异常是可以接受的。 So, I suppose this code is the correct way to filter exceptions: 因此,我想这段代码是过滤异常的正确方法:

try {
     ....
} catch (InvalidOperationException eInvalidOperationException) {
    if (eInvalidOperationException.TargetSite.DeclaringType == typeof(System.Diagnostics.Process)) {
        // Exception when accessing mWindowProcess
    } else
        throw;
} catch (ArgumentException eArgumentException) {
    if (eArgumentException.TargetSite.DeclaringType == typeof(System.Diagnostics.Process)) {
        // Exception when accessing mWindowProcess
    } else
        throw;
}

This work for my code until the code access to only the one Process instance ( mWindowProcess ); 这对我的代码有效,直到代码仅访问一个Process实例( mWindowProcess ); in the case multiple Process variables (not directly related with mWindowProcess ) throws those exceptions, they should be catched, and use the Exception.Data dictionary to notify the different situation. 如果多个Process变量(与mWindowProcess不直接相关)抛出这些异常,则应将其捕获 ,并使用Exception.Data字典来通知不同的情况。

The Exception class has a very effective control on the exception recognition. Exception类对异常识别具有非常有效的控制。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM