C#在线程之间编组COM对象

时间:2014-07-16 12:02:37

标签: c# multithreading com apartments

我对C#编组线程之间的COM对象感到非常困惑。为此,我有一个应用程序,它以任务并行方式加载一组文件。我使用StaTaskScehduler使用COM对象加载文件。加载COM对象后,我将对象存储在一个中心列表中。

然后,我再次使用STATaskScheduler尝试对此数据执行一些处理。但是在这一点上我遇到了一个问题。我收到如下例外情况:

An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in MadCat.exe

Additional information: COM object that has been separated from its underlying RCW cannot be used

现在我的理解是我收到此错误,因为该对象尚未编组到新线程中。我认为这是C#为你做的事情吗?

如何在一个线程中创建一个公寓线程COM对象,然后从另一个线程使用它?

我在这里咆哮错误的树吗?我甚至不能将Sta公寓用于我的线程吗?我可以保证对象永远不会同时从多个线程访问。任何想法都非常感激。

编辑 :COM对象定义如下:

[
    coclass,
    threading( apartment ),
    vi_progid( [Namespace.Class] ),
    progid( [Namespace.Class].6 ),
    version( 6.0 ),
    uuid( GUID_C[Class] ),
    helpstring( [Class]" Class" )
]

所以根据我的理解,这是一个公寓线程对象,对吧?我刚试过使用一个没有设置公寓状态的修改任务调度程序(MTA默认情况下?)。当我在一个线程中创建它并从另一个线程中使用它时,这个对象似乎确实有效。这样安全还是会以某种其他方式再次咬我?

COM的线程模型总是让我感到困惑:/

1 个答案:

答案 0 :(得分:3)

看起来你正在使用Stephen Toub的StaTaskScheduler作为一些“有状态”逻辑的一部分,其中你的COM对象生活在StartNew边界。如果是这种情况,请确保在同一StaTaskScheduler STA线程上创建并使用这些对象,而不在其外部。那么你根本不必担心COM编组。不用说,您应该只使用一个主题创建StaTaskScheduler,即numberOfThreads:1

这就是我的意思:

var sta = new StaTaskScheduler(numberOfThreads:1);

var comObjects = new { Obj = (ComObject)null };

Task.Factory.StartNew(() =>
{
    // create COM object
    comObjects.Obj = (ComObject)Activator.CreateInstance(
        Type.GetTypeFromProgID("Client.ProgID"));
}, CancellationToken.None, TaskCreationOptions.None, sta);

//...

for(int i=0; i<10; i++)
{
    var result = await Task.Factory.StartNew(() =>
    {
        // use COM object
        return comObjects.Obj.Method();    
    }, CancellationToken.None, TaskCreationOptions.None, sta);
}

如果Obj.Method()返回另一个COM对象,您应该将结果保存在同一个StaTaskScheduler的“公寓”中,并从那里访问它:

var comObjects = new { Obj = (ComObject)null, Obj2 = (AnotherComObject)null };
//...
await Task.Factory.StartNew(() =>
{
    // use COM object
    comObjects.Obj2 = comObjects.Obj.Method();    
}, CancellationToken.None, TaskCreationOptions.None, sta);

如果您还需要处理由Obj提供的事件,请检查: