使用COM互操作处理对象生命周期的最有效方法是什么?

时间:2022-10-17 23:26:00

I have a Windows Workflow application that uses classes I've written for COM automation. I'm opening Word and Excel from my classes using COM.

我有一个Windows Workflow应用程序,它使用我为COM自动化编写的类。我正在使用COM从我的课程中打开Word和Excel。

I'm currently implementing IDisposable in my COM helper and using Marshal.ReleaseComObject(). However, if my Workflow fails, the Dispose() method isn't being called and the Word or Excel handles stay open and my application hangs.

我目前正在我的COM助手中使用Marshal.ReleaseComObject()实现IDisposable。但是,如果我的工作流失败,则不会调用Dispose()方法,并且Word或Excel句柄保持打开状态并且我的应用程序挂起。

The solution to this problem is pretty straightforward, but rather than just solve it, I'd like to learn something and gain insight into the right way to work with COM. I'm looking for the "best" or most efficient and safest way to handle the lifecycle of the classes that own the COM handles. Patterns, best practices, or sample code would be helpful.

这个问题的解决方案非常简单,但不仅仅是解决它,我想学习一些东西并深入了解使用COM的正确方法。我正在寻找处理拥有COM句柄的类的生命周期的“最佳”或最有效和最安全的方法。模式,最佳实践或示例代码会很有帮助。

2 个解决方案

#1


1  

I can not see what failure you have that does not calls the Dispose() method. I made a test with a sequential workflow that contains only a code activity which just throws an exception and the Dispose() method of my workflow is called twice (this is because of the standard WorkflowTerminated event handler). Check the following code:

我看不出你没有调用Dispose()方法的失败。我使用顺序工作流进行测试,该工作流只包含一个只引发异常的代码活动,并且我的工作流的Dispose()方法被调用两次(这是因为标准的WorkflowTerminated事件处理程序)。检查以下代码:

Program.cs

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

Workflow1.cs

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

Am I missing something? Concerning the lifecycle-related methods of an Activity (and consequently of a Workflow) object, please check this post: Activity "Lifetime" Methods. If you just want a generic article about disposing, check this.

我错过了什么吗?关于活动(以及工作流)对象的生命周期相关方法,请查看以下帖子:活动“生命周期”方法。如果您只想要一篇关于处理的通用文章,请检查一下。

#2


0  

Basically, you should not rely on hand code to call Dispose() on your object at the end of the work. You probably have something like this right now:

基本上,您不应该依赖手动代码在工作结束时调用对象上的Dispose()。你现在可能有这样的事情:

MyComHelper helper = new MyComHelper();
helper.DoStuffWithExcel();
helper.Dispose();
...

Instead, you need to use try blocks to catch any exception that might be triggered and call dispose at that point. This is the canonical way:

相反,您需要使用try块来捕获可能触发的任何异常并在此时调用dispose。这是规范的方式:

MyComHelper helper = new MyComHelper();
try
{
    helper.DoStuffWithExcel();
}
finally()
{
    helper.Dispose();
}

This is so common that C# has a special construct that generates the same exact code [see note] as shown above; this is what you should be doing most of the time (unless you have some special object construction semantics that make a manual pattern like the above easier to work with):

这很常见,C#有一个特殊的构造,可以生成相同的代码[见注释],如上所示;这是你应该在大多数时间做的事情(除非你有一些特殊的对象构造语义,使上面的手动模式更容易使用):

using(MyComHelper helper = new MyComHelper())
{
    helper.DoStuffWithExcel();
}

EDIT:
NOTE: The actual code generated is a tiny bit more complicated than the second example above, because it also introduces a new local scope that makes the helper object unavailable after the using block. It's like if the second code block was surrounded by { }'s. That was omitted for clarify of the explanation.

编辑:注意:生成的实际代码比上面的第二个例子稍微复杂一点,因为它还引入了一个新的本地范围,使得帮助对象在使用块之后不可用。就像第二个代码块被{}包围一样。为了澄清解释,省略了这一点。

#1


1  

I can not see what failure you have that does not calls the Dispose() method. I made a test with a sequential workflow that contains only a code activity which just throws an exception and the Dispose() method of my workflow is called twice (this is because of the standard WorkflowTerminated event handler). Check the following code:

我看不出你没有调用Dispose()方法的失败。我使用顺序工作流进行测试,该工作流只包含一个只引发异常的代码活动,并且我的工作流的Dispose()方法被调用两次(这是因为标准的WorkflowTerminated事件处理程序)。检查以下代码:

Program.cs

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

Workflow1.cs

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

Am I missing something? Concerning the lifecycle-related methods of an Activity (and consequently of a Workflow) object, please check this post: Activity "Lifetime" Methods. If you just want a generic article about disposing, check this.

我错过了什么吗?关于活动(以及工作流)对象的生命周期相关方法,请查看以下帖子:活动“生命周期”方法。如果您只想要一篇关于处理的通用文章,请检查一下。

#2


0  

Basically, you should not rely on hand code to call Dispose() on your object at the end of the work. You probably have something like this right now:

基本上,您不应该依赖手动代码在工作结束时调用对象上的Dispose()。你现在可能有这样的事情:

MyComHelper helper = new MyComHelper();
helper.DoStuffWithExcel();
helper.Dispose();
...

Instead, you need to use try blocks to catch any exception that might be triggered and call dispose at that point. This is the canonical way:

相反,您需要使用try块来捕获可能触发的任何异常并在此时调用dispose。这是规范的方式:

MyComHelper helper = new MyComHelper();
try
{
    helper.DoStuffWithExcel();
}
finally()
{
    helper.Dispose();
}

This is so common that C# has a special construct that generates the same exact code [see note] as shown above; this is what you should be doing most of the time (unless you have some special object construction semantics that make a manual pattern like the above easier to work with):

这很常见,C#有一个特殊的构造,可以生成相同的代码[见注释],如上所示;这是你应该在大多数时间做的事情(除非你有一些特殊的对象构造语义,使上面的手动模式更容易使用):

using(MyComHelper helper = new MyComHelper())
{
    helper.DoStuffWithExcel();
}

EDIT:
NOTE: The actual code generated is a tiny bit more complicated than the second example above, because it also introduces a new local scope that makes the helper object unavailable after the using block. It's like if the second code block was surrounded by { }'s. That was omitted for clarify of the explanation.

编辑:注意:生成的实际代码比上面的第二个例子稍微复杂一点,因为它还引入了一个新的本地范围,使得帮助对象在使用块之后不可用。就像第二个代码块被{}包围一样。为了澄清解释,省略了这一点。