Skip to content

Define and enforce ownership semantics for raw RCWs passed into NetOffice wrappers #480

@jozefizso

Description

@jozefizso

Summary

NetOffice currently releases RCWs with Marshal.ReleaseComObject() from its wrapper lifetime layer. This is only safe if NetOffice exclusively owns every RCW it wraps. Public constructors and UnderlyingObject make it easy to share the same RCW with raw interop code, dynamic code, or another wrapper stack.

Background

The CLR creates and maintains Runtime Callable Wrappers (RCWs) for COM objects. The runtime keeps one RCW per COM object per process/apartment context, and Marshal.ReleaseComObject() decrements the RCW reference count. Microsoft documents that improper use of ReleaseComObject() can break other managed users of the same RCW and can cause hard-to-debug failures if release happens during active calls.

NetOffice adds COMProxyShare as a NetOffice-level reference counter over the RCW and releases the proxy at count zero.

Relevant code paths:

  • Source/NetOffice/COMProxyShare.cs: MarshalReleaseComObject()
  • Source/NetOffice/COMObject.cs: constructors accepting object comProxy
  • Source/NetOffice/COMDynamicObject.cs: constructors accepting object comProxy
  • Source/NetOffice/Interfaces/ICOMObjectProxy.cs: UnderlyingObject

Use Case

Applications often mix Office automation libraries, raw COM interop, and helper utilities. A raw RCW may be passed into NetOffice for convenience while the original caller still keeps and uses that RCW.

Example shape:

object rawWorkbook = excelInterop.ActiveWorkbook;
var noWorkbook = new NetOffice.ExcelApi.Workbook(parent, rawWorkbook);

noWorkbook.Dispose();

// rawWorkbook may now be disconnected although this code did not intend
// to transfer exclusive ownership to NetOffice.

Problem

NetOffice cannot know whether an incoming RCW is exclusively owned by NetOffice. Releasing the RCW from NetOffice can invalidate other managed references that still represent the same COM object. This is a mismatch between NetOffice's ownership layer and the CLR RCW architecture.

Suggested Fix

  • Document explicit ownership semantics for all APIs that accept raw COM proxies.
  • Consider separate "borrowed RCW" wrapping mode that does not call Marshal.ReleaseComObject().
  • Make ownership transfer explicit in constructor/factory names or options.
  • Add tests or samples showing safe and unsafe interop sharing patterns.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions