Guidelines for Exposing Functionality to COM
The common language runtime provides rich support for interoperating with COM components. A COM component can be used from within a managed type and a managed instance can be used by a COM component. This support is the key to moving unmanaged code to managed code one piece at a time; however, it does present some issues for class library designers. In order to fully expose a managed type to COM clients, the type must expose functionality in a way that is supported by COM and abides by the COM versioning contract.
Mark managed class libraries with the ComVisibleAttribute attribute to indicate whether COM clients can use the library directly or whether they must use a wrapper that shapes the functionality so that they can use it.
Types and interfaces that must be used directly by COM clients, such as to host in an unmanaged container, should be marked with the ComVisible(true) attribute. The transitive closure of all types referenced by exposed types should be explicitly marked as ComVisible(true); if not, they will be exposed as IUnknown.
Note Members of a type can also be marked as ComVisible(false); this reduces exposure to COM and therefore reduces the restrictions on what a managed type can use.
Types marked with the ComVisible(true) attribute cannot expose functionality exclusively in a way that is not usable from COM. Specifically, COM does not support static methods or parameterized constructors. Test the type's functionality from COM clients to ensure correct behavior. Make sure that you understand the registry impact for making all types cocreateable.
Marshal By Reference
Marshal-by-reference objects are Remotable Objects. Object remoting applies to three kinds of types:
- Types whose instances are copied when they are marshaled across an AppDomain boundary (on the same computer or a different computer). These types must be marked with the Serializable attribute.
- Types for which the runtime creates a transparent proxy when they are marshaled across an AppDomain boundary (on the same computer or a different computer). These types must ultimately be derived from System.MarshalByRefObject Class.
- Types that are not marshaled across AppDomains at all. This is the default.
Follow these guidelines when using marshal by reference:
- By default, instances should be marshal-by-value objects. This means that their types should be marked as Serializable.
- Component types should be marshal-by-reference objects. This should already be the case for most components, because the common base class, System.Component Class, is a marshal-by-reference class.
- If the type encapsulates an operating system resource, it should be a marshal-by-reference object. If the type implements the IDisposable Interface it will very likely have to be marshaled by reference. System.IO.Stream derives from MarshalByRefObject. Most streams, such as FileStreams and NetworkStreams, encapsulate external resources, so they should be marshal-by-reference objects.
- Instances that simply hold state should be marshal-by-value objects (such as a DataSet).
- Special types that cannot be called across an AppDomain (such as a holder of static utility methods) should not be marked as Serializable.