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

Goz*_*Goz 5 c# com multithreading apartments

我对 C# marshal 的 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
Run Code Online (Sandbox Code Playgroud)

现在我的理解是我收到此错误是因为该对象尚未编组到新线程中。我认为这是 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" )
]
Run Code Online (Sandbox Code Playgroud)

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

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

nos*_*tio 4

看起来您正在使用 Stephen Toub 的StaTaskScheduler作为某些“有状态”逻辑的一部分,其中您的 COM 对象跨越StartNew边界。如果是这种情况,请确保在同一StaTaskSchedulerSTA 线程上创建和使用这些对象,而不要在该线程之外的任何地方。那么您就根本不必担心 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);
}
Run Code Online (Sandbox Code Playgroud)

如果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);
Run Code Online (Sandbox Code Playgroud)

如果您还需要处理源自 的事件Obj,请检查以下内容: