Click here to Skip to main content
15,892,697 members
Articles / Desktop Programming / ATL

Classic Shell

Rate me:
Please Sign up or sign in to vote.
4.99/5 (135 votes)
23 Feb 2010MIT33 min read 970.8K   10K   195  
Classic Start menu and other shell features for Windows 7 and Vista.
// Classic Shell (c) 2009-2010, Ivo Beltchev
// The sources for Classic Shell are distributed under the MIT open source license

#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atlstr.h>
#include <commctrl.h>
#include <shlobj.h>

#include "ClassicStartMenuDLL\ClassicStartMenuDLL.h"

#define HOOK_EXPLORER // when this is not defined the start menu runs directly in this process (for debugging)

#if defined(BUILD_SETUP) && !defined(HOOK_EXPLORER)
#define HOOK_EXPLORER // make sure it is defined in Setup
#endif

static HHOOK g_StartHook;
static HWND g_StartButton;

static void UnhookStartMenu( void )
{
	if (g_StartHook)
		UnhookWindowsHookEx(g_StartHook);
	g_StartHook=NULL;
}

static HWND HookStartMenu( void )
{
	HMODULE hHookModule=GetModuleHandle(L"ClassicStartMenuDLL.dll");

	// find the Progman window and the start button
	HWND progWin=FindWindowEx(NULL,NULL,L"Progman",NULL);
	if (!progWin) return NULL; // the Progman window may not be created yet (if Explorer is currently restarting)

	DWORD process;
	DWORD thread=GetWindowThreadProcessId(progWin,&process);

	g_StartButton=FindStartButton(process);
	if (!g_StartButton) return NULL; // the start button may not be created yet (if Explorer is currently restarting)

#ifdef HOOK_EXPLORER
	// install hooks in the explorer process
	thread=GetWindowThreadProcessId(g_StartButton,NULL);
	g_StartHook=SetWindowsHookEx(WH_GETMESSAGE,HookInject,hHookModule,thread);
	PostMessage(g_StartButton,WM_NULL,0,0); // make sure there is one message in the queue

	return NULL;
#else
	return ToggleStartMenu(g_StartButton,false);
#endif
}

static UINT g_TaskbarCreatedMsg; // the "TaskbarCreated" message

// CStartHookWindow is a hidden window that waits for the "TaskbarCreated" message and rehooks the explorer process
// Also when the start menu wants to shut down it sends WM_CLOSE to this window, which unhooks explorer and exits

const int WM_OPEN=WM_USER+10;

const int TIMER_HOOK=1;

class CStartHookWindow: public CWindowImpl<CStartHookWindow>
{
public:

	DECLARE_WND_CLASS(L"ClassicStartMenu.CStartHookWindow")

	BEGIN_MSG_MAP( CStartHookWindow )
		MESSAGE_HANDLER( WM_OPEN, OnOpen )
		MESSAGE_HANDLER( WM_CLOSE, OnClose )
		MESSAGE_HANDLER( WM_CLEAR, OnClear )
		MESSAGE_HANDLER( WM_TIMER, OnTimer )
		MESSAGE_HANDLER( g_TaskbarCreatedMsg, OnTaskbarCreated )
	END_MSG_MAP()

protected:
	// Handler prototypes:
	//  LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	//  LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
	//  LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
	LRESULT OnOpen( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
	LRESULT OnClose( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
	LRESULT OnClear( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
	LRESULT OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
	LRESULT OnTaskbarCreated( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled );
};

LRESULT CStartHookWindow::OnOpen( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	if (g_StartButton) ::PostMessage(g_StartButton,RegisterWindowMessage(L"ClassicStartMenu.StartMenuMsg"),wParam,lParam);
	return 0;
}

LRESULT CStartHookWindow::OnClose( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	UnhookStartMenu();
	PostQuitMessage(0);
	return 0;
}

LRESULT CStartHookWindow::OnClear( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	UnhookStartMenu();
	return 0;
}

LRESULT CStartHookWindow::OnTaskbarCreated( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	SetTimer(TIMER_HOOK,100);
	return 0;
}

LRESULT CStartHookWindow::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	if (wParam==TIMER_HOOK)
	{
		UnhookStartMenu();
		HookStartMenu();
		if (g_StartHook)
			KillTimer(TIMER_HOOK);
	}
	return 0;
}

int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrCmdLine, int nCmdShow )
{
	int open=-1;
	if (wcsstr(lpstrCmdLine,L"-togglenew")!=NULL) open=-2;
	else if (wcsstr(lpstrCmdLine,L"-toggle")!=NULL) open=0;
	else if (wcsstr(lpstrCmdLine,L"-open")!=NULL) open=1;
	// prevent multiple instances from hooking the same explorer process
	HWND progWin=FindWindowEx(NULL,NULL,L"Progman",NULL);
	DWORD process;
	DWORD thread=GetWindowThreadProcessId(progWin,&process);
#ifdef HOOK_EXPLORER
	wchar_t mutexName[256];
	swprintf_s(mutexName,L"ClassicStartMenu.Mutex.%08x",process);
	HANDLE hMutex=CreateMutex(NULL,FALSE,mutexName);
	if (GetLastError()==ERROR_ALREADY_EXISTS || GetLastError()==ERROR_ACCESS_DENIED)
	{
		if (open>=0)
		{
			AllowSetForegroundWindow(process);
			HWND hwnd=FindWindow(L"ClassicStartMenu.CStartHookWindow",L"StartHookWindow");
			if (hwnd) PostMessage(hwnd,WM_OPEN,open,0);
		}
		if (open==-2 && progWin)
		{
			AllowSetForegroundWindow(process);
			PostMessage(progWin,WM_SYSCOMMAND,SC_TASKLIST,'CLSM');
		}
		return 0;
	}
#endif
	OleInitialize(NULL);
	g_TaskbarCreatedMsg=RegisterWindowMessage(L"TaskbarCreated");
	ChangeWindowMessageFilter(g_TaskbarCreatedMsg,MSGFLT_ADD);
	CStartHookWindow window;
	window.Create(NULL,NULL,L"StartHookWindow",WS_POPUP);

	MSG msg;
	HWND menu=HookStartMenu();
#ifdef HOOK_EXPLORER
	if (open>=0) window.PostMessage(WM_OPEN,open,0);
	while (GetMessage(&msg,0,0,0))
#else
	while (IsWindow(menu) && GetMessage(&msg,0,0,0))
#endif
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	window.DestroyWindow();
	OleUninitialize();
	return 0;
}

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, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
United States United States
Ivo started programming in 1985 on an Apple ][ clone. He graduated from Sofia University, Bulgaria with a MSCS degree. Ivo has been working as a professional programmer for over 12 years, and as a professional game programmer for over 10. He is currently employed in Pandemic Studios, a video game company in Los Angeles, California.

Comments and Discussions