我有一个Windows Workflow应用程序,它使用为COM自动化编写的类。 我正在使用COM从类中打开Word和Excel。
我当前在COM帮助器中实现IDisposable,并使用Marshal.ReleaseComObject()。 但是,如果我的工作流程失败,则不会调用Dispose()方法,并且Word或Excel句柄保持打开状态,并且我的应用程序将挂起。
这个问题的解决方案非常简单,但我不仅要解决问题,还想学点东西,并深入了解使用COM的正确方法。 我正在寻找"最佳"或最高效,最安全的方式来处理拥有COM句柄的类的生命周期。 模式,最佳做法或示例代码将很有帮助。
我看不到您没有调用Dispose()方法的故障。我使用顺序工作流进行了测试,该工作流仅包含一个引发异常的代码活动,并且我的工作流的Dispose()方法被调用了两次(这是由于使用了标准的WorkflowTerminated事件处理程序)。检查以下代码:
Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{
waitHandle.Set();
};
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
Console.ReadKey();
}
} |
工作流程1.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public sealed partial class Workflow1: SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
this.codeActivity1.ExecuteCode += new System.EventHandler(this.codeActivity1_ExecuteCode);
}
[DebuggerStepThrough()]
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Throw ApplicationException.");
throw new ApplicationException();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Here you must free your resources
// by calling your COM helper Dispose() method
Console.WriteLine("Object disposed.");
}
}
} |
我想念什么吗?关于Activity(以及因此导致的Workflow)对象与生命周期相关的方法,请查看以下文章:Activity"生命周期"方法。如果您只想了解有关处理的一般文章,请选中此复选框。
基本上,您不应在工作结束时依靠手动代码在对象上调用Dispose()。您现在可能有这样的事情:
1 2 3 4
| MyComHelper helper = new MyComHelper();
helper.DoStuffWithExcel();
helper.Dispose();
... |
相反,您需要使用try块来捕获可能触发的任何异常,然后在此时调用dispose。这是规范的方式:
1 2 3 4 5 6 7 8 9
| MyComHelper helper = new MyComHelper();
try
{
helper.DoStuffWithExcel();
}
finally()
{
helper.Dispose();
} |
这是如此普遍,以至于C#具有一种特殊的结构,可以生成相同的精确代码(请参见注释),如上所示。这是您大多数时候应该做的(除非您具有一些特殊的对象构造语义,这些语义使上面的手动模式更易于使用):
1 2 3 4
| using(MyComHelper helper = new MyComHelper())
{
helper.DoStuffWithExcel();
} |
编辑:
注意:生成的实际代码比上面的第二个示例稍微复杂一点,因为它还引入了一个新的本地作用域,该局部作用域在using块之后使助手对象不可用。就像第二个代码块被{}包围一样。为了澄清说明,省略了该步骤。