# Contract ComWrappers

This contract is for getting information related to COM wrappers.

## APIs of contract

``` csharp
// Get the address of the external COM object
TargetPointer GetComWrappersIdentity(TargetPointer rcw);
// Given a ccw pointer, return the managed object wrapper
public TargetPointer GetManagedObjectWrapperFromCCW(TargetPointer ccw);
// Given a managed object wrapper, return the comwrappers pointer
public TargetPointer GetComWrappersObjectFromMOW(TargetPointer mow);
// Given a managed object wrapper, return its reference count
public long GetMOWReferenceCount(TargetPointer mow);
// Get the COM identity (IUnknown pointer) for a managed object wrapper
TargetPointer GetIdentityForMOW(TargetPointer mow);
// Get all managed object wrappers for a given managed object
List<TargetPointer> GetMOWs(TargetPointer obj, out bool hasMOWTable);
// Determine if a pointer represents a ComWrappers RCW
public bool IsComWrappersRCW(TargetPointer rcw);
// Get the ComWrappers RCW for a given managed object, or null if none exists
TargetPointer GetComWrappersRCWForObject(TargetPointer obj);
```

## Version 1

<!-- BEGIN GENERATED: usage contract=ComWrappers version=c1 -->
### Data descriptors used

| Data Descriptor | Field | Type | Meaning |
| --- | --- | --- | --- |
| `Array` | *(type size)* | `uint32` | Size of the fixed portion of an array object |
| `ComInterfaceEntry` | *(type size)* | `uint32` | Size in bytes of each user-defined COM interface entry |
| `ComInterfaceEntry` | `IID` | `nuint` | The interface GUID |
| `ComWrappersVtablePtrs` | *(type size)* | `uint32` | Size of vtable pointers array |
| `InternalComInterfaceDispatch` | *(type size)* | `uint32` | Size in bytes of each dispatch block; used to determine the number of vtable entries per block |
| `InternalComInterfaceDispatch` | `Entries` | `pointer` | Start of vtable entry pointers within the dispatch block |
| `ManagedObjectWrapperHolderObject` | `WrappedObject` | `pointer` | Address of the wrapped object |
| `ManagedObjectWrapperHolderObject` | `Wrapper` | `pointer` | Pointer to the ManagedObjectWrapperLayout |
| `ManagedObjectWrapperLayout` | `Dispatches` | `pointer` | Pointer to the dispatch section (InternalComInterfaceDispatch array) |
| `ManagedObjectWrapperLayout` | `Flags` | `int32` | CreateComInterfaceFlagsEx flags |
| `ManagedObjectWrapperLayout` | `RefCount` | `int64` | Reference count of the managed object wrapper |
| `ManagedObjectWrapperLayout` | `UserDefined` | `pointer` | Pointer to array of ComInterfaceEntry |
| `ManagedObjectWrapperLayout` | `UserDefinedCount` | `int32` | Number of user-defined COM interface entries |
| `NativeObjectWrapperObject` | `ExternalComObject` | `pointer` | Address of the external COM object |
| ``System.Collections.Generic.List`1`` | `_items` | `pointer` | Backing array containing the list elements |
| ``System.Collections.Generic.List`1`` | `_size` | `int32` | Number of elements currently stored in the list |

### Global variables used

| Global | Type | Meaning |
| --- | --- | --- |
| `ComWrappers.s_allManagedObjectWrapperTable` | `pointer` | Address of the ComWrappers managed static table that maps managed objects to their wrappers |
| `ComWrappers.s_nativeObjectWrapperTable` | `pointer` | Address of the ComWrappers managed static table that maps native objects to their wrappers |
| `ComWrappersVtablePtrs` | `pointer` | Pointer to struct containing ComWrappers-related function pointers |
| `DispatchThisPtrMask` | `pointer` | Used to mask low bits of CCW pointer to the nearest valid address from which to read a managed object wrapper |
| `System.Runtime.InteropServices.ComWrappers.s_allManagedObjectWrapperTable` | `pointer` | Address of the ComWrappers managed static table that maps managed objects to their wrappers |
| `System.Runtime.InteropServices.ComWrappers.s_nativeObjectWrapperTable` | `pointer` | Address of the ComWrappers managed static table that maps native objects to their wrappers |

### Contracts used

| Contract Name |
| --- |
| `ConditionalWeakTable` |
| `ManagedTypeSource` |
| `Object` |
| `PlatformMetadata` |
<!-- END GENERATED: usage contract=ComWrappers version=c1 -->


### Managed types used

| Fully-qualified name | Module | Members read | Purpose |
| --- | --- | --- | --- |
| `System.Runtime.InteropServices.ComWrappers+NativeObjectWrapper` | `System.Private.CoreLib` | (type layout only) | Identifies RCWs by comparing their MethodTable against this type |
| `System.Runtime.InteropServices.ComWrappers` | `System.Private.CoreLib` | `static s_allManagedObjectWrapperTable`, `static s_nativeObjectWrapperTable` | Per-object lookups for managed-object-wrapper enumeration and RCW retrieval |

### Contract Constants

| Name | Type | Purpose | Value |
| --- | --- | --- | --- |
| `CallerDefinedIUnknown` | int | Flag bit for `CreateComInterfaceFlagsEx` indicating caller-defined IUnknown | `1` |
| `IID_IUnknown` | Guid | The IID for IUnknown | `00000000-0000-0000-C000-000000000046` |


``` csharp

public TargetPointer GetComWrappersIdentity(TargetPointer address)
{
    return _target.ReadPointer(address + /* NativeObjectWrapperObject::ExternalComObject offset */);
}

private bool GetComWrappersCCWVTableQIAddress(TargetPointer ccw, out TargetPointer vtable, out TargetPointer qiAddress)
{
    vtable = TargetPointer.Null;
    qiAddress = TargetPointer.Null;

    // read two levels of indirection from the ccw to get the code pointer into qiAddress
    // if read fails, return false

    qiAddress = CodePointerUtils.AddressFromCodePointer(qiAddress, _target);
    return true;
}

private bool IsComWrappersCCW(TargetPointer ccw)
{
    if (!GetComWrappersCCWVTableQIAddress(ccw, out _, out TargetPointer qiAddress))
        return false;

    TargetPointer comWrappersVtablePtrs = _target.ReadGlobalPointer("ComWrappersVtablePtrs");

    return /* qiAddress matches any entry in ComWrappersVtablePtrs */ ;
}

public TargetPointer GetManagedObjectWrapperFromCCW(TargetPointer ccw)
{
    if (!IsComWrappersCCW(ccw))
        return TargetPointer.Null;
    try
    {
        return _target.ReadPointer(ccw & _target.ReadGlobalPointer("DispatchThisPtrMask"));
    }
    catch (VirtualReadException)
    {
        return TargetPointer.Null;
    }
}

public TargetPointer GetComWrappersObjectFromMOW(TargetPointer mow)
{
    TargetPointer mowHolderObject = /* read two layers of indirection from MOW */;
    return mowHolderObject + /* ManagedObjectWrapperHolderObject::WrappedObject offset */;
}

public long GetMOWReferenceCount(TargetPointer mow)
{
    return target.Read<long>(mow + /* ManagedObjectWrapperLayout::RefCount offset */);
}

private TargetPointer IndexIntoDispatchSection(int index, TargetPointer dispatches)
{
    // InternalComInterfaceDispatch contains a _thisPtr followed by EntriesPerThisPtr vtable entries.
    // EntriesPerThisPtr = (sizeof(InternalComInterfaceDispatch) / pointerSize) - 1
    uint dispatchSize = /* InternalComInterfaceDispatch size */;
    uint entriesPerThisPtr = (dispatchSize / target.PointerSize) - 1;

    TargetPointer dispatch = dispatches + (index / entriesPerThisPtr) * dispatchSize;
    TargetPointer entries = dispatch + /* InternalComInterfaceDispatch::Entries offset */;

    return entries + (index % entriesPerThisPtr) * target.PointerSize;
}

public TargetPointer GetIdentityForMOW(TargetPointer mow)
{
    // Read the ManagedObjectWrapperLayout fields
    int flags = target.Read<int>(mow + /* ManagedObjectWrapperLayout::Flags offset */);
    int userDefinedCount = target.Read<int>(mow + /* ManagedObjectWrapperLayout::UserDefinedCount offset */);
    TargetPointer userDefined = target.ReadPointer(mow + /* ManagedObjectWrapperLayout::UserDefined offset */);
    TargetPointer dispatches = target.ReadPointer(mow + /* ManagedObjectWrapperLayout::Dispatches offset */);

    if ((flags & CallerDefinedIUnknown) == 0)
    {
        // Standard IUnknown is at the runtime-defined slot (right after user-defined entries)
        return IndexIntoDispatchSection(userDefinedCount, dispatches);
    }

    // Search user-defined entries for IID_IUnknown
    for (int i = 0; i < userDefinedCount; i++)
    {
        Guid iid = /* read GUID at userDefined + i * ComInterfaceEntry size + ComInterfaceEntry::IID offset */;
        if (iid == IID_IUnknown)
            return IndexIntoDispatchSection(i, dispatches);
    }

    return TargetPointer.Null;
}

public List<TargetPointer> GetMOWs(TargetPointer obj, out bool hasMOWTable)
{
    // Look up the static field ComWrappers.s_allManagedObjectWrapperTable via ManagedTypeSource
    // Use the ConditionalWeakTable contract to find the List<ManagedObjectWrapperHolderObject> value
    // Iterate the list and return each holder's Wrapper pointer (the ManagedObjectWrapperLayout address)
}

public bool IsComWrappersRCW(TargetPointer rcw)
{
    // Get method table from rcw using Object contract GetMethodTableAddress
    // Resolve ComWrappers+NativeObjectWrapper via ManagedTypeSource by fully-qualified name
    // Then compare the rcw method table with the method table found by name
}

public TargetPointer GetComWrappersRCWForObject(TargetPointer obj)
{
    // Look up the static field ComWrappers.s_nativeObjectWrapperTable via ManagedTypeSource
    // Use the ConditionalWeakTable contract to find the value associated with obj
    // If found, return the NativeObjectWrapper reference (tagged with low bit by caller)
    TargetPointer cwtTable = /* address of ComWrappers.s_nativeObjectWrapperTable static field */;
    if (cwtTable == TargetPointer.Null)
        return TargetPointer.Null;

    target.Contracts.ConditionalWeakTable.TryGetValue(cwtTable, obj, out TargetPointer rcw);

    return rcw;
}
```
