The RCW will hold a reference to the classic COM Server, and will release that reference when it is garbage collected. At that point the classic COM Server will be Released. When the NGWS runtime client requests a new interface the wrapper will handle the call through QueryInterface(). When a new interface is passed to a NGWS runtime client via any means the wrapper will be responsible for releasing the reference on that interface when the wrapper is garbage collected.
The NGWS runtime only maintains one RCW per object. It distinguishes between objects by comparing the values of the IUnknown pointers it receives. When an interface is being passed into the NGWS runtime, the Runtime queries on that interface for IUnknown, then checks that interface pointer against the ones already being wrapped.
If the classic COM Server being wrapped returns an error HRESULT from a method call, the RCW will throw an exception to the NGWS runtime client. If the classic COM Server provides additional error information (via ISupportErrorInfo and GetLastError) the extended error information will also be propogated into the exception object.
The failure HRESULT returned from a call is converted to an exception and thrown by the RCW on return from the call. The NGWS runtime client can then catch the exception just like any other exception. The type of exception thrown depends on the HRESULT returned. The table below contains a complete list all NGWS runtime exceptions along with the HRESULTs return values that will cause those exceptions to be thrown. If a failure HRESULT that is not listed in the table is returned, the runtime throws an ApplicationException. The client can determine the HRESULT that caused the exception by examining the ErrorCode field of the Exception object.
HResult Returned | Exception thrown by RCW |
---|---|
COR_E_ACCESS | AccessException |
COR_E_AMBIGUOUSMATCH | AmbiguousMatchException |
MSEE_E_APPDOMAINUNLOADED | AppDomainUnloadedException |
MSEE_E_APPDOMAINUNLOADINPROGRESS | AppDomainUnloadInProgressException |
COR_E_APPLICATION | ApplicationException |
COR_E_ARGUMENT | ArgumentException |
E_POINTER | ArgumentNullException |
COR_E_ARGUMENTOUTOFRANGE | ArgumentOutOfRangeException |
COR_E_ARITHMETIC | ArithmeticException |
COR_E_ARRAYTYPEMISMATCH | ArrayTypeMismatchException |
MSEE_E_CLASSUNLOADED | ClassUnloadedException |
COR_E_COMEMULATE_ERROR | COMEmulateException |
COR_E_CONTEXTMARSHAL | ContextMarshalException |
COR_E_CORE | CoreException |
NTE_FAIL | CryptographicException |
COR_E_DIRECTORYNOTFOUND | DirectoryNotFoundException |
COR_E_DIVIDEBYZERO | DivideByZeroException |
COR_E_DUPLICATEWAITOBJECT | DuplicateWaitObjectException |
COR_E_ENDOFSTREAM | EndOfStreamException |
COR_E_TYPELOAD | EntryPointNotFoundException |
COR_E_EXCEPTION | Exception |
COR_E_EXECUTIONENGINE | ExecutionEngineException |
COR_E_FIELDACCESS | FieldAccessException |
COR_E_FILENOTFOUND | FileNotFoundException |
COR_E_FORMAT | FormatException |
COR_E_INDEXOUTOFRANGE | IndexOutOfRangeException |
COR_E_INVALIDCAST | InvalidCastException |
COR_E_INVALIDCOMOBJECT | InvalidComObjectException |
COR_E_INVALIDFILTERCRITERIA | InvalidFilterCriteriaException |
COR_E_INVALIDOLEVARIANTTYPE | InvalidOleVariantTypeException |
COR_E_INVALIDOPERATION | InvalidOperationException |
COR_E_IO | IOException |
COR_E_METHODACCESS | MethodAccessException |
COR_E_MISSINGFIELD | MissingFieldException |
COR_E_MISSINGMANIFESTRESOURCE | MissingManifestResourceException |
COR_E_MISSINGMEMBER | MissingMemberException |
COR_E_MISSINGMETHOD | MissingMethodException |
COR_E_MULTICASTNOTSUPPORTED | MulticastNotSupportedException |
COR_E_NOTFINITENUMBER | NotFiniteNumberException |
E_NOTIMPL | NotImplementedException |
COR_E_NOTSUPPORTED | NotSupportedException |
COR_E_NULLREFERENCE | NullReferenceException |
E_OUTOFMEMORY | OutOfMemoryException |
COR_E_OVERFLOW | OverflowException |
COR_E_PATHTOOLONG | PathTooLongException |
COR_E_RANK | RankException |
COR_E_REFLECTIONTYPELOAD | ReflectionTypeLoadException |
COR_E_REMOTING | RemotingException |
COR_E_SAFEARRAYTYPEMISMATCH | SafeArrayTypeMismatchException |
COR_E_SECURITY | SecurityException |
COR_E_SERIALIZATION | SerializationException |
COR_E_STACKOVERFLOW | StackOverflowException |
COR_E_SINKHRONIZATIONLOCK | SinkhronizationLockException |
COR_E_SYSTEM | SystemException |
COR_E_TARGET | TargetException |
COR_E_TARGETINVOCATION | TargetInvocationException |
COR_E_TARGETPARAMCOUNT | TargetParameterCountException |
COR_E_THREADABORTED | ThreadAbortException |
COR_E_THREADINTERRUPTED | ThreadInterruptedException |
COR_E_THREADSTATE | ThreadStateException |
COR_E_THREADSTOP | ThreadStopException |
COR_E_TYPELOAD | TypeLoadException |
COR_E_TYPEINITIALIZATION | TypeInitializationException |
COR_E_VERIFIER | VerifierException |
COR_E_WEAKREFERENCE | WeakReferenceException |
All other HResults | ComException |
Only failure HRESULTs (those with the severity bit set) will cause exceptions to be thrown by the RCW. Success HRESULTs do not cause exceptions to be thrown.
To get additional information about the exception, the NGWS runtime client would typically examine the fields of the Exception object thats thrown. In order for the runtime to provide meaningful information about the error, the COM server must implement the IErrorInfo interface. If the COM server supports IErrorInfo, the runtime will use the information provided through IErrorInfo to initialize the Exception object. If the classic COM server does not support IErrorInfo, the Exception object is initialized with default values.
Exception Field | COM Source of information |
---|---|
ErrorCode | HResult returned from call |
HelpLink | If IErrorInfo->HelpContext() is non-zero, the String formed by concatenating IErrorInfo->GetHelpFile() and # and IErrorInfo->GetHelpContext(). Otherwise the string returned from IErrorInfo->GetHelpFile(). |
InnerException | Always Null |
Message | String returned from IErrorInfo->GetDescription() |
Source | String returned from IErrorInfo->GetSource() |
StackTrace | The stack trace |
TargetSite | The name of the method that returned the failing HResult. |
If the classic COM Server being wrapped implements IProvideClassInfo, the RCW will use this type information to provide support for Reflection as well as interrogating the type information for its own purposes. Type information may also be obtained from the Registry, or from an IDispatch implementation. As mentioned in Section 2, Class information is required before an object can be wrapped.
Dispinterfaces exposed by COM servers can only be accessed from the NGWS runtime through reflection. Reflection is the mechanism for doing object inspection and dynamic invocation. You cannot make a direct method call on an RCW if the RCW is only wrapping a dispinterface.
A dual interface or dispinterface may declare properties as well as methods. All properties have corresponding accessor methods for setting or getting the property values. When converting a type library description of an interface with properties to metadata, a property as well as one or more accessor methods for that property is created. The type library conversion process applies the following transformations to property accessor methods.
For example:
In a Type Library:
interface ISample : IDispatch { [propget, HRESULT prop1([out, retval] short *pVal); [propput, HRESULT prop1([in] short newVal); [propget, HRESULT prop2([out, retval] IFoo **pVal); [propputref, HRESULT prop2([in] IFoo *newVal); [propget, HRESULT prop3([out, retval] IFoo **ppIFoo); [propput, HRESULT prop3([in] BSTR *text); [propputref, HRESULT prop3([in] IFoo *pIFoo); }
In Managed C++:
interface ISample { __property short prop1 {get {}; set {};}; // propget, propset __property IFoo prop2 {get {}; set {};}; // propget, propset __property IFoo prop3 {get {}; set()}; // propget, propputref Letprop3(String text); // propput }
Public Property Get Prop1() As Integer End Property Public Property Let Prop1(val as Integer) End Property Public Property Get Prop2() As IFoo End Property Public Property Let Prop2(val as IFoo) End Property Public Property Get Prop3() As IFoo End Property Public Property Let Prop3(val as IFoo) End Property Public Setprop3Value(String as Text) End Property
The runtime also provides a custom marshaler for marshaling the ITypeInfo interface to the System.Type class. Any unmanaged method that uses the ITypeInfo interface will automatically use the marshaler when the method is exposed to the NGWS runtime. The TlbImp utility will convert all references to ITypeInfo to System.Type on import.
For example, the following unmanaged type:
interface TypeProvider { HRESULT SetType([in] ITypeInfo *t); HRESULT SetTypeRef([in, out] ITypeInfo **t); HRESULT GetType([out, retval] ITypeInfo **t); }
Is converted to:
interface TypeProvider { void SetType(System.Type t); void SetTypeRef(ref System.Type t); System.Type GetType(); }
A custom marshaler is provided by the runtime that provides a bridge between the COM interface IEnumVariant and the the NGWS frameworks interface IEnumerator. The custom marshaler causes method parameters and properties of type IEnumVariant to be exposed to the NGWS runtime as the IEnumerator interface. So for example, a COM collection object with a property of type IEnumVariant whose unmanaged signature is:
HResult _NewEnum(IEnumVariant *pEnum);
Would have the following signature when exported to a COM type library.
IEnumerator _NewEnum ();
The marshaler automatically delegates the calls from the NGWS runtime interface to the COM interface in the following way:
bool IEnumerator.MoveNext()
Calls IEnumVariant.Next(1, ) and caches the Variant returned as an Object. If Next returns S_FALSE, the method returns False otherwise the method returns true.
Object IEnumerator.Current()
Returns the object cached when MoveNext was called.
For additional details on custom marshaling see the Custom Marshaling spec.
In the NGWS frameworks, enumerators are provided by the System.Collections.IEnumerable interface.
public interface IEnumerable { IEnumerator GetEnumerator(); }
The interface has a method called GetEnumerator that returns an enumerator. When imported by typelib importer, any COM coclass that provides an enumerator will also appear to implement the IEnumerable interface. For example, the Fields interface defined in a typelib as follows:
interface Fields : { [id(0xfffffffc)] HRESULT _NewEnum([out, retval] IUnknown** ppvObj); };
Would be imported by the typelib importer as:
interface Fields : implements IEnumerable { Object _NewEnum(); IEnumerator GetEnumerator(); }
Notice that the GetEnumerator method is automatically added to the interface and that the interface implements IEnumerable. The proper way of obtaining the enumerator for any managed type (including RCWs) is to cast the type to IEnumerable and then call IEnumerable.GetEnumerator.