Click here to Skip to main content
15,880,543 members
Articles / Programming Languages / C++

A Context Menu Handler for Windows Users That Can't Let Go

Rate me:
Please Sign up or sign in to vote.
4.79/5 (13 votes)
31 May 20062 min read 126.2K   2.2K   39  
A context menu handler that makes it easier to work with paths, command windows, and program arguments from within Explorer.
// cmdcmx.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "cmdcmx.h"
#include "clsfac.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//this part is only done once
//if you need to use the GUID in another file, just include cmdcmx_guid.h
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#include "cmdcmx_guid.h"

// g_szExtTitle: Global buffer stores the title string used with DLL registration.
LPCTSTR g_szExtTitle = TEXT("CMDCMX");

// REGSTRUCT: Used to assist in DLL registration.
typedef struct{
   HKEY  hRootKey;
   LPCTSTR lpszSubKey;
   LPCTSTR lpszValueName;
   LPCTSTR lpszData;
}REGSTRUCT, *LPREGSTRUCT;

// g_DllRefCount: Global reference count to this instance.
UINT g_DllRefCount = NULL;

// CcmdcmxApp

BEGIN_MESSAGE_MAP(CcmdcmxApp, CWinApp)
END_MESSAGE_MAP()

// CcmdcmxApp construction

CcmdcmxApp::CcmdcmxApp()
{
}

CcmdcmxApp::~CcmdcmxApp()
{
}

// The one and only CcmdcmxApp object
CcmdcmxApp theApp;

// CcmdcmxApp initialization
BOOL CcmdcmxApp::InitInstance()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	// New instance... start the ref count at 0.
	g_DllRefCount = 0;

	// Standard WIN32 and MFC init stuff.
	InitCommonControls();
	CWinApp::InitInstance();

	// Some handle magic to make AfxGetResourceHandle() handle work properly.
	afxCurrentResourceHandle = afxCurrentInstanceHandle = AfxGetInstanceHandle();

	return TRUE;
}

STDAPI DllCanUnloadNow(VOID)
{
	return (g_DllRefCount ? S_FALSE : S_OK);
}

STDAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID *ppReturn )
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	*ppReturn = NULL;

	//if we don't support this classid, return the proper error code
	if(!IsEqualCLSID(rclsid, CLSID_CMDCMX))
		return CLASS_E_CLASSNOTAVAILABLE;
   
	//create a CClassFactory object and check it for validity
	CClassFactory *pClassFactory = new CClassFactory();
	if(NULL == pClassFactory)
		return E_OUTOFMEMORY;
   
	//get the QueryInterface return for our return value
	HRESULT hResult = pClassFactory->QueryInterface(riid, ppReturn);

	//call Release to decrement the ref count - creating the object set it to one 
	//and QueryInterface incremented it - since its being used externally (not by 
	//us), we only want the ref count to be 1
	pClassFactory->Release();

	//return the result from QueryInterface
	return hResult;
}

STDAPI DllRegisterServer(VOID)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	int      i;
	HKEY     hKey;
	LRESULT  lResult;
	DWORD    dwDisp;
	TCHAR    szSubKey[MAX_PATH];
	TCHAR    szCLSID[MAX_PATH];
	TCHAR    szModule[MAX_PATH];
	LPWSTR   pwsz;

	//get the CLSID in string form
	StringFromIID(CLSID_CMDCMX, &pwsz);

	if(pwsz)
	{
		lstrcpy(szCLSID, pwsz);

		//free the string
		LPMALLOC pMalloc;
		CoGetMalloc(1, &pMalloc);
		if(pMalloc)
		{
			pMalloc->Free(pwsz);
			pMalloc->Release();
		}
	}

	//get this DLL's path and file name
	GetModuleFileName(AfxGetInstanceHandle(), szModule, ARRAYSIZE(szModule));

	//register the CLSID entries
	REGSTRUCT ClsidEntries[] = {	HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s"),                  NULL,                   g_szExtTitle,
									HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\InprocServer32"),  NULL,                   TEXT("%s"),
									HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\InprocServer32"),  TEXT("ThreadingModel"), TEXT("Apartment"),
									HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\DefaultIcon"),     NULL,                   TEXT("%s,0"),
									NULL,                NULL,                               NULL,                   NULL};

	for(i = 0; ClsidEntries[i].hRootKey; i++)
	{
		//Create the sub key string.
		wsprintf(szSubKey, ClsidEntries[i].lpszSubKey, szCLSID);

		lResult = RegCreateKeyEx(ClsidEntries[i].hRootKey,szSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisp);
   
		if(NOERROR == lResult)
		{
			TCHAR szData[MAX_PATH];

			//if necessary, create the value string
			wsprintf(szData, ClsidEntries[i].lpszData, szModule);
			lResult = RegSetValueEx( hKey, ClsidEntries[i].lpszValueName, 0, REG_SZ, (LPBYTE)szData, (lstrlen(szData) + 1) * sizeof(TCHAR));
			RegCloseKey(hKey);
		}
		else
			return SELFREG_E_CLASS;
	}

	// Add the context menu handler for all files.
	lstrcpyn( szSubKey, TEXT("*\\shellex\\ContextMenuHandlers\\"), ARRAYSIZE(szSubKey));
	lstrcat( szSubKey, szCLSID );

	lResult = RegCreateKeyEx( HKEY_CLASSES_ROOT,szSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisp);

	if(NOERROR == lResult)
	{
		TCHAR szData[MAX_PATH];

		//Create the value string.
		lstrcpyn(szData, g_szExtTitle, ARRAYSIZE(szData));

		lResult = RegSetValueEx( hKey,NULL,0,REG_SZ,(LPBYTE)szData,(lstrlen(szData) + 1) * sizeof(TCHAR));
   
		RegCloseKey(hKey);
	}
	else
		return SELFREG_E_CLASS;

	// Add the context menu handler for all folders
	lstrcpyn( szSubKey, TEXT("Folder\\shellex\\ContextMenuHandlers\\"), ARRAYSIZE(szSubKey));
	lstrcat( szSubKey, szCLSID );
	lResult = RegCreateKeyEx( HKEY_CLASSES_ROOT,szSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisp);

	if(NOERROR == lResult)
	{
		TCHAR szData[MAX_PATH];

		//Create the value string.
		lstrcpyn(szData, g_szExtTitle, ARRAYSIZE(szData));
		lResult = RegSetValueEx(hKey,NULL,0,REG_SZ,(LPBYTE)szData,(lstrlen(szData) + 1) * sizeof(TCHAR));
   		RegCloseKey(hKey);
	}
	else
		return SELFREG_E_CLASS;

	//If running on NT, register the extension as approved.
	OSVERSIONINFO  osvi;

	osvi.dwOSVersionInfoSize = sizeof(osvi);
	GetVersionEx(&osvi);

	if(VER_PLATFORM_WIN32_NT == osvi.dwPlatformId)
	{
		lstrcpyn( szSubKey, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), ARRAYSIZE(szSubKey));

		lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE,szSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisp);

		if(NOERROR == lResult)
		{
			TCHAR szData[MAX_PATH];

			//Create the value string.
			lstrcpyn(szData, g_szExtTitle, ARRAYSIZE(szData));

			lResult = RegSetValueEx(hKey,szCLSID,0,REG_SZ,(LPBYTE)szData,(lstrlen(szData) + 1) * sizeof(TCHAR));
      
			RegCloseKey(hKey);
		}
		else
			return SELFREG_E_CLASS;
	}

	//tell the shell that the folder has been added.
	LPITEMIDLIST   pidlDesktop, pidlMyComputer;

	SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop);
	SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer);
	SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlDesktop, 0);
	SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlMyComputer, 0);

	IMalloc  *pMalloc;
	if(S_OK == SHGetMalloc(&pMalloc))
	{
		pMalloc->Free(pidlDesktop);
		pMalloc->Free(pidlMyComputer);
		pMalloc->Release();
	}

	return S_OK;
}

STDAPI DllUnregisterServer(VOID)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	LPWSTR   pwsz;
	TCHAR    szCLSID[MAX_PATH];
	TCHAR    szSubKey[MAX_PATH];

	//get the CLSID in string form
	StringFromIID(CLSID_CMDCMX, &pwsz);

	if(pwsz)
	{
		lstrcpy(szCLSID, pwsz);

		//free the string
		LPMALLOC pMalloc;
		CoGetMalloc(1, &pMalloc);
		if(pMalloc)
		{
			pMalloc->Free(pwsz);
			pMalloc->Release();
		}
	}
	else
	{
		return E_FAIL;
	}

	//delete the object's registry entries
	wsprintf(szSubKey, TEXT("CLSID\\%s"), szCLSID);
	SHDeleteKey(HKEY_CLASSES_ROOT, szSubKey);

	lstrcpyn( szSubKey, TEXT("*\\shellex\\ContextMenuHandlers\\"), ARRAYSIZE(szSubKey));
	lstrcat( szSubKey, szCLSID );
	SHDeleteKey(HKEY_CLASSES_ROOT, szSubKey);

	lstrcpyn( szSubKey, TEXT("Folder\\shellex\\ContextMenuHandlers\\"), ARRAYSIZE(szSubKey));
	lstrcat( szSubKey, szCLSID );
	SHDeleteKey(HKEY_CLASSES_ROOT, szSubKey);

	//delete the approved extensions on NT
	OSVERSIONINFO  osvi;

	osvi.dwOSVersionInfoSize = sizeof(osvi);
	GetVersionEx(&osvi);

	if(VER_PLATFORM_WIN32_NT == osvi.dwPlatformId)
	{
		LRESULT  lResult;
		HKEY     hKey;

		lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"),0,KEY_SET_VALUE,&hKey);

		if(NOERROR == lResult)
		{
			TCHAR szData[MAX_PATH];

			//Create the value string.
			lstrcpyn(szData, g_szExtTitle, ARRAYSIZE(szData));

			lResult = RegDeleteValue( hKey, szCLSID);
      
			RegCloseKey(hKey);
		}
		else
			return SELFREG_E_CLASS;
	}

	//tell the shell that the folder has been removed.
	LPITEMIDLIST   pidlDesktop,	pidlMyComputer;

	SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop);
	SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer);
	SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlDesktop, 0);
	SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlMyComputer, 0);

	IMalloc  *pMalloc;
	if(S_OK == SHGetMalloc(&pMalloc))
	{
		pMalloc->Free(pidlDesktop);
		pMalloc->Free(pidlMyComputer);
		pMalloc->Release();
	}

	return S_OK;
}

void CcmdcmxApp::ShowError( UINT id, HRESULT hRes, LPCTSTR lpctstrContext )
{
	TCHAR szMsg[512] = TEXT("");

	if( ::LoadString(AfxGetResourceHandle(), id, szMsg, (sizeof(szMsg)/sizeof(TCHAR))) )
	{
		TCHAR szErrorMsg[256] = TEXT("");
		TCHAR szMsgResult[512] = TEXT("");

		if( hRes != ERROR_SUCCESS )
		{
			ASSERT(lpctstrContext);

			if( ::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, hRes,
				   MAKELANGID( LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
				   szErrorMsg, (sizeof(szErrorMsg)/sizeof(TCHAR)), NULL ) )
			{
				::wsprintf( szMsgResult, szMsg, szErrorMsg, lpctstrContext );
			}
			else
			{
				::wsprintf( szMsgResult, szMsg, TEXT("Unknown"), lpctstrContext );
			}
			::MessageBox( ::GetDesktopWindow(), szMsgResult, g_szExtTitle, MB_OK|MB_ICONSTOP );
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
16yrs of GUI programming experience gained at: (most recent first) BlackBall, Veritas, Seagate Software, Arcada, Stac, Mountain, and Emerald Systems.

Languages/Scripting: C, C++, JAVA, BASIC, JAVASCRIPT, HTML, XML, PHP, and SQL

Tools: MS Visual Studio, MS Visual SourceSafe, CVS, PVCS, Bounds Checker, VMWare, ToDoList, InstallShield, and Office Applications

Libraries and API: RTL, STL, WIN32, MFC, ATL, .NET, ActiveX, DirectX, COM, DCOM, Shell Extensions, and Shell Namespaces

Strengths: Honest, communicative, keen eye for usability, good at estimating workload and completion dates, ready to take on grunt work, team player, experienced working with QA, localization, Tech Pubs, Sales, and Marketing teams.

Comments and Discussions