如何在 C# 程序中以 RAII 风格管理 COM 对象运行时?

发布于 2024-11-28 17:05:35 字数 1378 浏览 4 评论 0原文

我的 C# 程序使用具有大量各种接口和子对象的 COM 组件。问题是每次我检索某个 COM 接口时,都会创建一个 RCW,并且该 RCW 存在的时间未知(直到被 GC 收集)。每个 RCW 都在 COM 服务器中保存一些对象。

COM 组件是一个进程外服务器,因此它的对象驻留在一个单独的重量级进程中,该进程在释放驻留在其中的所有对象之前不会终止。我希望所有对象尽快释放,这样我就可以确定,一旦释放最后一个对象,进程外服务器进程就会终止并且不再消耗系统资源。这不是我的偏执——拥有多个消耗系统资源(尤其是内存)的重量级进程对性能来说确实很糟糕。

到目前为止,我制作了一个实现 IDisposable 的通用类,接受对对象的引用(实际上是 RCW)并调用 Marshal.FinalReleaseComObjectDispose 中 执行。

为了这个问题,让我们暂时忽略使用 FinalReleaseComObject() 可能出现的问题 - 我知道它们并且可以容忍它们。

真正的问题是我被迫对所有对象使用 using 或编写 finally 子句并在那里调用 Dispose() 。它可以工作,但是代码由于大量额外的接线而变得相当混乱,这让我很烦恼。

例如,我需要将一个字符串分配给 someObject.Params.ColorParams.Hint 属性 - 我不能只写,

someObject.Params.ColorParams.Hint = whatever;

因为对于每个访问器都会有一个我需要释放的 COM 对象,所以我有这个:

using( MyWrapper<IParams> params = new MyWrapper<IParams>( someObject.Params ) ) {
    using( MyWrapper<IColorParams> colorParams =
         new MyWrapper<IColorParams>( params.Controlled ) )
    {
        colorParams.Controlled.Hint = whatever;
    }
}

这是最简单的例子 - 有时我需要访问五层深度的东西,然后我编写一组五层深度的 using 语句。

有没有更优雅的解决方案?

My C# program uses a COM component that has a ton of various interfaces and subobjects. The problem is each time I retrieve some COM interface an RCW is created and that RCW exists for unknown time (until collected by GC). Each RCW holds some object in the COM server.

The COM component is an out-proc server, so its objects reside in a separate quite heavy-weight process that won't terminate until all objects residing in it are released. I want all object to be releases ASAP so that I know for sure that once I released the last object the out-proc server process terminates and no longer consumes system resources. That's not my paranoia - having several heavyweight processes consuming system resources, especially memory, is really bad for performance.

So far I crafted a generic class that implements IDisposable, accepts a reference to the object (RCW in fact) and calls Marshal.FinalReleaseComObject in Dispose implementation.

For the sake of this question let's temporarily ignore possible problems arising from using FinalReleaseComObject() - I'm aware of them and can tolerate them.

The real problem is I'm forced to either use using for all objects or to write finally clauses and call Dispose() there. It works, but code gets rather cluttered with lots of extra wiring and that bothers me big time.

For example, I need to assign a string to someObject.Params.ColorParams.Hint property - I can't just write

someObject.Params.ColorParams.Hint = whatever;

because for each accessor there will be a COM object that I need to get released, so I have this instead:

using( MyWrapper<IParams> params = new MyWrapper<IParams>( someObject.Params ) ) {
    using( MyWrapper<IColorParams> colorParams =
         new MyWrapper<IColorParams>( params.Controlled ) )
    {
        colorParams.Controlled.Hint = whatever;
    }
}

and that's the most trivial example - sometimes I need to access something five levels deep and then I write a set of using statements five level deep.

Is there a more elegant solution to the problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

橙味迷妹 2024-12-05 17:05:35

请参阅 Hans Passant 的这个关于使用 IDisposable 清理 Excel 互操作对象的答案

总之,您根本不需要 IDisposable。只需在所有 COM 引用超出范围后显式调用垃圾回收即可,即不是从使用它们的函数而是从该函数的调用方调用垃圾回收。

Please see this answer to Clean up Excel Interop Objects with IDisposable by Hans Passant.

In summary, you don't need IDisposable at all. Just explicitly invoke garbage collection after all of your COM references have gone out of scope – i.e. not from the function that uses them but from the caller of that function.

素染倾城色 2024-12-05 17:05:35

我不确定我是否完全理解这个问题,但如果我这样做(代码混乱),有两种可能的解决方案:

包装方法 - 您可以定义一个简单的方法:

TResult Exec<TResult>(Func<TResult> func, params MarshalByRefObject comObjects)
{
  TResult res = default(TResult);
  try
  {
    res = func.Invoke();
  }
  catch (Exception e) { /* Log? */ }
  finally
  {
    foreach (MarshalByRefObject combObj in comObjects)
    {    /* release resources using common interface or reflection */ }
  }
}

非常类似于以前的方法,仅使用众多 AOP 之一执行此操作的框架(例如 PostSharp

I'm not sure I fully understand the problem, but if I do (code clutter) there are 2 possible solutions:

A wrapper method - You could define a simple method:

TResult Exec<TResult>(Func<TResult> func, params MarshalByRefObject comObjects)
{
  TResult res = default(TResult);
  try
  {
    res = func.Invoke();
  }
  catch (Exception e) { /* Log? */ }
  finally
  {
    foreach (MarshalByRefObject combObj in comObjects)
    {    /* release resources using common interface or reflection */ }
  }
}

Very much like the previous method, only using one of the many AOP frameworks to do it (e.g PostSharp)

等风来 2024-12-05 17:05:35

c# 和确定性资源释放通常充其量是很麻烦的。
进程外 COM 对象的释放就是问题的一个例子。

如果您乐意编写一些 C++/cli 代码,它至少可以选择托管堆对象的堆栈语义。这避免了当对象超出范围时编译器为您调用 dispose 时使用或最终包装的需要。

当然,它确实为您带来了一种比 c#、头文件 .、->、:: 等更嘈杂的语言。

通常,如果您的代码能够在 using 块内的本地范围内创建和释放 COM 对象,我会留在 C# 并忍受使用上的麻烦。

如果您的代码需要将 COM 对象保留为成员变量,我将仅将这些类移至 c++/cli 并利用成员变量的堆栈语义,这些变量将自动为您链接 dispose 调用。

c# and deterministic release of resources is generally troublesome at best.
Release of out of process COM objects is one example of the problem.

If you are happy to write some C++/cli code it at least has the option of stack semantics for managed heap objects. This avoids the need for using or finally wrapping as the compiler calls dispose for you when the object drops out of scope.

Of course it does bring you an otherwise noisier language than c#, header files ., ->, :: etc.

As a rule if your code is able to create and release the COM objects at local scope within a using block, I would stay in C# and put up with the using hassle.

If your code needs to keep COM objects as member variables I would move just those classes to c++/cli and take advantage of stack semantics for member variables that will automatically chain dispose calls for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文