Listing 1: Calling a .NET component from C++

// Initialize the COM system
::CoInitialize(NULL);
// Retrieve the CLSID for Hello.HelloClass
CLSID clsidHello;
LPOLESTR szHello = L"Hello.HelloClass";
::CLSIDFromProgID(szHello, &clsidHello);
// Create the IDispatch instance
LPDISPATCH lpDispHello = NULL;
::CoCreateInstance(
	clsidHello, NULL, CLSCTX_INPROC_SERVER,
	IID_IDispatch, (LPVOID*)&lpDispHello);
// Retrive the DISPID for the SayHelloTo method
LPOLESTR szHelloFn = L"SayHelloTo";
DISPID dispSayHelloTo;
lpDispHello->GetIDsOfNames(
	IID_NULL, &szHelloFn, 1, LOCALE_SYSTEM_DEFAULT, &dispSayHelloTo);
// Prepare the parameters for the SayHelloTo method
DISPPARAMS dispparams = { NULL, NULL, 0, 0 };
dispparams.cArgs = 1;
dispparams.rgvarg = new VARIANT[dispparams.cArgs];
dispparams.cNamedArgs = 0;
dispparams.rgvarg[0].vt = VT_BSTR;
dispparams.rgvarg[0]. bstrVal = ::SysAllocString(OLESTR("Jack"));
// The result will be stored here
VARIANT vtRes;
::VariantInit(&vtRes);
// Invoke the method
lpDispHello->Invoke(
	dispSayHelloTo, IID_NULL, LOCALE_SYSTEM_DEFAULT,
	DISPATCH_METHOD, &dispparams, &vtRes, NULL, NULL);
// Here vtRes.bstrVal is equal to "Hello there, Jack"
// Get rid of our component
lpDispHello->Release();
// Don't forget to uninitialize COM
::CoUninitialize();

Listing 2: A ClassInterfaceType.None class implementing two interfaces

using System.Runtime.InteropServices;
namespace Test {
	/// <summary>
	/// Define an interface derived from IUnknown
	/// </summary>
	[Guid(...), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IInterfaceOne {
		void TestMethodOne();
	}
	/// <summary>
	/// Throw in another interface, derived from IDispatch...
	/// </summary>
	[Guid(...), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
	public interface IInterfaceTwo {
		void TestMethodTwo();
	}
	/// <summary>
	/// And yet another interface, this time an internal one.
	/// Even if it is defined with InterfaceIsIUnknown, it
	/// will not be exposed to COM, because it is internal.
	/// </summary>
	[Guid(...), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	internal interface INotExposedInterface {
		void SomeMethod();
	}
	/// <summary>
	/// This class is defined as ClassInterfaceType.None, and
	/// implements our interfaces. All members defined by
	/// these interfaces (except INotExposedInterface, of course)
	/// are exposed to COM clients. All other methods defined in
	/// this class are NOT exposed at all, even if they are public.
	/// That is, ClassInterfaceType.None is used for a class
	/// that will expose only ComVisible interfaces to the outside
	/// world. All other methods and members are not exposed.
	/// </summary>
	[ClassInterface(ClassInterfaceType.None)]
	public class TestClass :
		IInterfaceOne, IInterfaceTwo, INotExposedInterface {
		// This method IS exposed, because it comes
		// from the IInterfaceOne interface
		public void TestMethodOne() {}
		// This method IS exposed, because it comes 
		// from the IInterfaceTwo interface
		public int TestMethodTwo() {}
		// This method, even if implements the method
		// with the same name from INotExposedInterface
		// will not be exposed because that interface
		// is marked as internal.
		public void SomeMethod() {}
		// This method IS NOT exposed, because it is
		// defined by a class marked as ClassInterfaceType.None,
		// and doesn't come from a ComVisible interface.
		public void InvisibleMethod() {}
	}
}

Listing 3: The definition for the IOleCommandTarget standard interface

[ComVisible(true), ComImport()]
[Guid("B722BCCB-4E68-101B-A2BC-00AA00404770")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleCommandTarget {
	[PreserveSig()]
	int QueryStatus(ref Guid pguidCmdGroup, uint cCmds,
		[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
		OLECMD[] prgCmds, ref OLECMDTEXT pCmdText);
	[PreserveSig()]
	int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdExecOpt,
		ref object pvaIn, ref object pvaOut);
}

Listing 4: Class used to discover the correspondence between .NET types and COM types

using System.Runtime.InteropServices;
namespace Test {
	[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface ITestTypes {
		string GetAString();
		int GetAnInt();
		long GetALong();
		sbyte GetSByte();
		byte GetByte();
		short GetShort();
		ushort GetUShort();
		uint GetUInt();
		char GetChar();
		ulong GetULong();
		bool GetBool();
		decimal GetDecimal();
		float GetFloat();
		double GetDouble();
		object GetAnObject();
	}
	[ClassInterface(ClassInterfaceType.None)]
	public class TestTypes : ITestTypes {
		public string GetAString() { return string.Empty; }
		public int GetAnInt() { return Int32.MaxValue; }
		public long GetALong() { return Int64.MaxValue; }
		public sbyte GetSByte() { return SByte.MaxValue; }
		public byte GetByte() { return Byte.MaxValue; }
		public short GetShort() { return short.MaxValue; }
		public ushort GetUShort() { return ushort.MaxValue; }
		public uint GetUInt() { return uint.MaxValue; }
		public char GetChar() { return char.MaxValue; }
		public ulong GetULong() { return ulong.MaxValue; }
		public bool GetBool() { return true; }
		public decimal GetDecimal() { return decimal.MaxValue; }
		public float GetFloat() { return float.MaxValue; }
		public double GetDouble() { return double.MaxValue; }
		public object GetAnObject() { return null; }
	}
}

Listing 5: Expose events to COM

using System.Runtime.InteropServices;
namespace Test {
	public delegate void HelloErrorHandler(string error);
	[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
	public interface IHello {
		string SayHelloTo(string name);
	}
	[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
	public interface IHelloEvents {
		[DispId(0x00000001)]
		void HelloError(string error);
	}
	[ClassInterface(ClassInterfaceType.None)]
	[ComSourceInterfaces(typeof(IHelloEvents))]
	public class HelloClass : IHello {
		event HelloErrorHandler HelloError;
		public string SayHelloTo(string name) {
			string realName = name.Trim();
			if (realName != string.Empty) {
				return "Hello there, " + realName;
			}
			if (HelloError != null) {
				HelloError("No one out there?");
			}
			return string.Empty;
		}
	}
}