views:

540

answers:

2

We have a COM component who’s implementation and interface definition exist in managed code but is driven by a native component. The managed component is returning a SafeArray back to the native code via the following method declaration.

interface IExample {
  <return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
  object[] DoSomeOperation()
}

The generated native signature properly passes this back as a SafeArray.

During a code review though we came up with some questions about calls to the resulting array with SafeArrayGetElement. The issue is whether or not SafeArrayGetElement returns a IUnknown instance which is AddRef'd or not. Essentially it boils down to which of the following is correct

Example 1:

CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));

Example 2:

IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));

The documentation is very thin on this subject. It only includes the following line.

If the data element is a string, object, or variant, the function copies the element in the correct way.

The definition of correct is a bit ambiguous.

+1  A: 

It should be AddRef:ed, but I don't have first-hand info that that's the case (e.g. I haven't read the source).

I think the documentation is pretty clear, though -- copying an interface pointer 'correctly' is AddRef:ing it.

If you want to be really sure, build a super-simple ATL COM object which implements IUnknown, stuff a number of them into a SAFEARRAY and put a breakpoint in CComObjectBase<>::InternalAddRef (if my memory serves). Then debug a call to SafeArrayGetElement and see if your breakpoint is hit.

Kim Gräsman
+2  A: 

The first method should be correct, and would be in line with handling of objects throughout COM, presumably the definition you found makes the assumption that the consumer knows the correct way.

The other items mentioned require it. Copying a VARIANT or a SAFEARRAY carries an implicit AddRef() when they contain objects. VARIANT doesn't require it when VT_BYREF is present, though.

VariantCopy @ MSDN
SafeArrayCopy @ MSDN

This behavior isn't intrinsic to SAFEARRAYs or VARIANTs as it is part of the rules of handling parameters in COM. There isn't anything stopping someone from trying to circumvent the rules, though.

For input parameters, it isn't the responsibility of the callee to AddRef() unless they intend to keep the interface pointer for further use. However, other cases of parameter use require it.

For example, interfaces placed in VARIANTs or other containers should have at least one AddRef() call applied, otherwise this would create issues when using VARIANTs as output parameters from COM methods, as transfer of data/references is one-way. The original object could expire by the time the call arrives at its destination. Similarly, marshaling an interface into a Stream requires an AddRef() as well.

Similarly, calling by reference requires at least one AddRef call as well. If this were not the case, then any suitable long-running call (say, through DCOM) may not arrive at its destination with the guarantee that the referenced object is still alive. Extra AddRef()/Release() calls are frequently skipped here, though, as the object should already be at 1+ due to creation in or before the calling scope.

If it is possible to modify the component, and your calls are in-process, then it may be desirable to use the GIT instead. This allows you to pass a token instead, and it will be easier to marshal the interface across COM apartments. The lifetime of the objects involved then becomes the responsibility of the caller through the duration of the call, and you'll be able to trap cases where an object could not be marshaled.

Creating the Global Interface Table @ MSDN

Also interesting is the footnote for BSTRs.

If the implementation of a function that takes a BSTR reference parameter assigns a new BSTR to the parameter, it must free the previously referenced BSTR.

String Manipulation Functions (COM)

meklarian