I've been working on an ActiveX control written in C++/CLI recently and I thought I'd share the COM registration function ([ComRegisterFunction()] attribute) I've written (which is called by regasm.exe when you're registering the assembly for COM Interop).
It does the following:
Creates the necessary keys to register as an ActiveX control.
Adds in the codebase keys so there is no need to pass the /codebase flag to regasm.exe.
Please note, if you're not including oleidl.h as part of your include hierarchy (which I am as I'm using the Windows SDK), then you'll need to define the following constants:
// Ref: http://msdn.microsoft.com/en-us/library/ms678497.aspx
const int OLEMISC_RECOMPOSEONRESIZE = 1;
const int OLEMISC_CANTLINKINSIDE = 16;
const int OLEMISC_INSIDEOUT = 128;
const int OLEMISC_ACTIVATEWHENVISIBLE = 256;
const int OLEMISC_SETCLIENTSITEFIRST = 131072;
Credit is given to the authors of the CSActiveX control upon which this code was originally based.
//
// we need to add some extra registry keys on top of the regasm.exe generated
// keys. this is because we are an ActiveX control (not just a plain COM control)
// and as such require certain keys and values to be present.
//
// we also add in the codebase entries (instead of passing /codebase to regasm.exe)
// this is so we avoid the overkill warnings that regasm.exe generates.
//
// as such to register this .NET assembly as a fully fledged ActiveX control
// we need to run:
//
// regasm.exe /tlb
//
void FTEngine::Register( Type^ t )
{
try
{
// Open the CLSID key of the control
RegistryKey^ keyCLSID = Registry::ClassesRoot->OpenSubKey( "CLSID\\" +
t->GUID.ToString("B"), true );
//////////////////////////////////////////////////////////////////////////
// Set "InprocServer32" to register a 32-bit in-process server.
// InprocServer32 =
// Ref: http://msdn.microsoft.com/en-us/library/ms683844.aspx
//
RegistryKey^ subkey = keyCLSID->OpenSubKey( "InprocServer32", true );
if( subkey )
{
// .NET runtime engine (mscoree.dll) for .NET assemblies
subkey->SetValue( nullptr, Environment::SystemDirectory + "\\mscoree.dll" );
// setup codebase flag as we're not in the GAC
subkey->SetValue( "CodeBase", Assembly::GetExecutingAssembly()->CodeBase );
// setup the version specific codebase flag
Version^ ver = Assembly::GetExecutingAssembly()->GetName()->Version;
subkey = subkey->OpenSubKey( String::Format( "{0}.{1}.{2}.{3}",
ver->Major, ver->Minor, ver->Build,
ver->Revision ), true );
if( subkey )
{
// setup codebase flag as we're not in the GAC
subkey->SetValue( "CodeBase",
Assembly::GetExecutingAssembly()->CodeBase );
}
}
//////////////////////////////////////////////////////////////////////////
// Create "Control" to identify it as an ActiveX Control.
// Ref: http://msdn.microsoft.com/en-us/library/ms680056.aspx
//
keyCLSID->CreateSubKey( "Control" );
//////////////////////////////////////////////////////////////////////////
// Create "MiscStatus" to specify how to create/display an object.
// MiscStatus =
// Ref: http://msdn.microsoft.com/en-us/library/ms683733.aspx
//
subkey = keyCLSID->CreateSubKey( "MiscStatus" );
int nMiscStatus = OLEMISC_RECOMPOSEONRESIZE +
OLEMISC_CANTLINKINSIDE + OLEMISC_INSIDEOUT +
OLEMISC_ACTIVATEWHENVISIBLE + OLEMISC_SETCLIENTSITEFIRST;
subkey->SetValue( "", nMiscStatus.ToString(), RegistryValueKind::String );
//////////////////////////////////////////////////////////////////////////
// Create "ToolBoxBitmap32" to identify the module name and the resource
// ID for a 16 x 16 bitmap as the toolbar button face.
// ToolBoxBitmap32 = .,
// Ref: http://msdn.microsoft.com/en-us/library/ms687316.aspx
//
subkey = keyCLSID->CreateSubKey( "ToolBoxBitmap32" );
// If you want different icons for each control in the assembly you
// can modify this section to specify a different icon each time.
// Each specified icon must be embedded as a win32 resource in the
// assembly; the default one is at the index 101, but you can use
// additional ones.
subkey->SetValue( "",
Assembly::GetExecutingAssembly()->Location + ", 101",
RegistryValueKind::String );
//////////////////////////////////////////////////////////////////////////
// Create "TypeLib" to specify the typelib GUID associated with the class.
//
subkey = keyCLSID->CreateSubKey("TypeLib");
Guid libId = Marshal::GetTypeLibGuidForAssembly( t->Assembly );
subkey->SetValue( "",
libId.ToString("B"),
RegistryValueKind::String );
//////////////////////////////////////////////////////////////////////////
// Create "Version" to specify the version of the control.
// Ref: http://msdn.microsoft.com/en-us/library/ms686568.aspx
//
subkey = keyCLSID->CreateSubKey( "Version" );
int nMajor, nMinor;
Marshal::GetTypeLibVersionForAssembly( t->Assembly, nMajor, nMinor);
subkey->SetValue( "",
String::Format( "{0}.{1}", nMajor, nMinor ) );
}
catch( Exception^ ex )
{
LogException( ex );
throw;
}
}