Programmatically registering and unregistering COM servers

In certain situations, there may be a need to programmatically register or unregister a COM server given just the name of the module that actually implements it. The specified server could be an InProcess COM server or an OutOfProcess COM server. As we all know, inprocess COM servers are registered by loading the DLL module and invoking the exported function DllRegisterServer(). The unregistration operation is performed by invoking DllUnregisterServer() instead.

OutOfProcess COM servers are registered by passing /REGSERVER on the command line for that executable. The corresponding unregistration is done by passing /UNREGSERVER on the command line. Given this, the only thing left is to detect if the file passed in is a DLL or an EXE. This is easily achieved by using the GetFileVersionInfo() Platform SDK function.

I have written a C++ function to achieve all of this and the code for the same follows. This code uses MFC but can be easily rewritten using std::basic_string() to make it usable within a pure ATL project.

HRESULT RegisterOleServer(const CString& strOleServerFile, bool bRegister) 
{ 
	CString strTempName = strOleServerFile; 

	DWORD dwFileType = VFT_UNKNOWN; 

	DWORD dwVerInfoBytes; 
	DWORD dwHandle; 

	LPBYTE pVerInfo = NULL; 

	VS_FIXEDFILEINFO* pFixedFileInfo = NULL; 
	UINT uiLength; 

	HRESULT hResult = S_OK; 


	// Detect if the server is an InProc or OutProc server. 
	dwVerInfoBytes = ::GetFileVersionInfoSize(strTempName.GetBuffer(0), &dwHandle); 

	if (dwVerInfoBytes == 0) 
		return HRESULT_FROM_WIN32(::GetLastError()); 


	// Allocate memory to hold the version information for the specified file. 
	try 
	{ 
	pVerInfo = new BYTE[dwVerInfoBytes]; 
	} 
	catch(CMemoryException* e) 
	{ 
	e->Delete(); 

	return E_OUTOFMEMORY; 
	} 


	// Determine the server type. 
	if (::GetFileVersionInfo(strTempName.GetBuffer(0), 0, dwVerInfoBytes, pVerInfo) && 
		::VerQueryValue(pVerInfo, _T("\\"), (LPVOID*)&pFixedFileInfo, &uiLength)) 
	{ 
		//if (AfxIsValidAddress(pFixedFileInfo, sizeof(VS_FIXEDFILEINFO))) 
		//{ 
		dwFileType = pFixedFileInfo->dwFileType; 
		//} 
	} 

	// Cleanup resources allocated while determing the server type. 
	delete pVerInfo; 


	// Register the Ole Server depending on whether it is an Exe or Dll. 
	switch (dwFileType) 
	{ 
		case VFT_APP: 
		{ 
			CString strRegServerCommand; 


			if (bRegister) 
			strRegServerCommand.Format(_T("%s /REGSERVER"), (LPCTSTR)strTempName); 
			else 
			strRegServerCommand.Format(_T("%s /UNREGSERVER"), (LPCTSTR)strTempName); 

			// Code for ExecuteCommand() follows this function. 
			hResult = ExecuteCommand(strRegServerCommand, 10000); 
		} 
		break; 

		case VFT_DLL: 
		{ 
			HINSTANCE hOleServerInst = NULL; 


			// Load the server dll into our process space. 
			hOleServerInst = ::LoadLibrary(strTempName); 

			if (hOleServerInst) 
			{ 
				HRESULT (STDAPICALLTYPE *pfnRegServer)(void); 


				if (bRegister) 
				{ 
					(FARPROC&)pfnRegServer = ::GetProcAddress(hOleServerInst, _T"DllRegisterServer")); 

				} 
				else 
				{ 
					(FARPROC&)pfnRegServer = ::GetProcAddress(hOleServerInst, _T"DllUnregisterServer")); 

				} 

				if (pfnRegServer) 
				{ 
					hResult = (*pfnRegServer)(); 
				} 
				else 
				{ 
					hResult = HRESULT_FROM_WIN32(::GetLastError()); 
				} 

				::CoFreeLibrary(hOleServerInst); 
			} 
			else 
			{ 
				hResult = HRESULT_FROM_WIN32(::GetLastError()); 
			} 
		} 
		break; 

		default: 
		hResult = E_INVALIDARG; 
		break; 
	} 

	return hResult; 
} 

HRESULT ExecuteCommand(CString& strCommand, DWORD dwTimeout) 
{ 
	STARTUPINFO si; 
	PROCESS_INFORMATION pi; 

	HRESULT hResult = S_OK; 


	// Initialize start-up information for the new process. 
	::ZeroMemory(&si, sizeof(si)); 
	si.cb = sizeof(si); 

	::ZeroMemory(&pi, sizeof(pi)); 

	if (::CreateProcess(NULL, strCommand.GetBuffer(0), NULL, NULL, FALSE, NULL, NULL, NULL, 
	&si, &pi)) 
	{ 
		if (dwTimeout) 
		{ 
			::WaitForSingleObject(pi.hProcess, dwTimeout); 

			::CloseHandle(pi.hProcess); 
			::CloseHandle(pi.hThread); 
		} 
	} 
	else 
		hResult = HRESULT_FROM_WIN32(::GetLastError()); 

		return hResult; 
} 



Comments

  • Can't Rely on Version Resource!

    Posted by Legacy on 03/01/1999 12:00am

    Originally posted by: Patrick Dell'Era

    There is a real problem attempting to identify the COM server as inproc 
    
    or outproc based on the version resource information. The most common
    problem will be found with process images that have no resource. Not
    likely? Unfortunately, proxy-stubs are generated without version
    resources (I suppose to make them as lightweight as possible?)

    Another problem seen is where a version resource misidentifies the
    image. Since the version resource is for informational as opposed to
    operational purposes, there is no obligation for accuracy! As an
    example, this could happen when an inproc server is converted to
    outproc but the version resource is not updated appropriately.

    What to do? Simply try loading the file as an inproc server and see
    what happens. If that fails, try running it as an outproc server. No
    harm in trying, as they say.

    Here's an example based on Jeremiah Talkar's original RegisterOleServer
    function:

    HRESULT RegisterOleServer(const CString& strOleServerFile,
    bool bRegister)
    {
    CString strTempName = strOleServerFile;
    HRESULT hResult = S_OK;
    HINSTANCE hOleServerInst = NULL;
    // Attempt to load the server dll into our process space.
    hOleServerInst = ::LoadLibrary(strOleServerFile);

    if (hOleServerInst)
    {
    HRESULT (STDAPICALLTYPE *pfnRegServer)(void);

    (FARPROC&)pfnRegServer = ::GetProcAddress(hOleServerInst,
    (bRegister ? _T("DllRegisterServer") :
    _T("DllUnregisterServer")));
    if (pfnRegServer)
    hResult = (*pfnRegServer)();
    else
    hResult = HRESULT_FROM_WIN32(::GetLastError());

    ::CoFreeLibrary(hOleServerInst);
    }
    else
    hResult = HRESULT_FROM_WIN32(::GetLastError());

    if( FAILED( hResult ) )
    {
    CString strRegServerCommand;

    strRegServerCommand.Format( _T("%s /%sREGSERVER"),
    (LPCTSTR)strTempName,
    (bRegister ? _T("") : _T("UN") ));

    // Code for ExecuteCommand() follows this function.
    hResult = ExecuteCommand(strRegServerCommand, 10000);
    }

    return hResult;
    }


    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • A modern mobile IT strategy is no longer an option, it is an absolute business necessity. Today's most productive employees are not tied to a desk, an office, or a location. They are mobile. And your company's IT strategy has to be ready to support them with easy, reliable, 24/7 access to the business information they need, from anywhere in the world, across a broad range of communication devices. Here's how some of the nation's most progressive corporations are meeting the many needs of their mobile workers …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds