subClass.c


/*
	$RCSfile: subClass.c $
	$Revision: 1.3 $
	$Date: 1997/03/13 15:49:42 $
	$Author: doomer $
	Copyright (c) 1997 John Dumais.  All rights reserved.
*/

#include <windows.h>
#include "subClass.h"

/*
	We  maintain information about the state of our application
	window and store that in a structure available only to
	our DLL.
*/
static struct SubClassInfo subClassInfo;

/*
	Our DLL's main entry point.  When we first get loaded into an
	application's process space, we find the window the application
	created in its main thread.  We then subclass that window, so that
	we get a chance to react to window messages before the application
	does.

	This function also gets called when an application is unloading
	the DLL.  In this case, we want to remove our subclass function.
	We remove the subclass function by restoring the original message-
	processing function.
*/
BOOL WINAPI	DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved){
	BOOL returnVal=TRUE;
	
	switch(reason){
		case DLL_PROCESS_ATTACH:
			subClassInfo.windowOfInterest=findApplicationWindow(&subClassInfo);
			if(subClassInfo.windowOfInterest){
				if(IT_FAILED(SubclassAppWindow(&subClassInfo, SubclassProc))){
					returnVal=FALSE;
				}
			}
			else{	/* subClassInfo.windowOfInterest */
				returnVal=FALSE;
			}

			break;
		case DLL_PROCESS_DETACH:
			if(subClassInfo.windowIsSubclassed){
				SubclassAppWindow(&subClassInfo, subClassInfo.originalWndProc);
			}
			
			break;
		default:

			break;
	}

	return returnVal;
}

/*
	We need to be able to identify the window created by the attaching
	application's main thread.	We make a call to EnumThreadWindows()
	which will cause the window system to continuously call a callback
	function with the window identifier for all the current
	top-level windows.  Our callback function is called enumWindowsCallback.
*/
HWND findApplicationWindow(struct SubClassInfo *scInfo){
	BOOL result;
	
	scInfo->windowOfInterest=(HWND)0;
	scInfo->windowThread=GetCurrentThreadId();

	result=EnumThreadWindows(scInfo->windowThread,
							 enumWindowsCallback,
							 (LPARAM)scInfo);
	
	return scInfo->windowOfInterest;
}

/*
	As the window system calls this function with a window identifier,
	we find out which thread created the associated window by calling
	GetWindowThreadProcessId().  When we find the thread that matches
	the main thread for the application that loaded our DLL, we know
	that we have found the window our application created.
*/
BOOL CALLBACK enumWindowsCallback(HWND aWnd, LPARAM windowThreadInfo){
	DWORD windowThreadId;
	struct SubClassInfo *sci;
	
	sci=(struct SubClassInfo *)windowThreadInfo;
	
	windowThreadId=GetWindowThreadProcessId(aWnd, (LPDWORD)0);
	
	if (windowThreadId == (DWORD)sci->windowThread) {
		sci->windowOfInterest=aWnd;

		/* we found our window.  stop looking */
		return(FALSE);
	}

	/* this isn't our window.  continue looking */
	return(TRUE);
}

/*
	This is the function the windows system will call each time there
	is an event ready for processing.  We can elect to forward that message
	on to the application's original message processing function or not.
	In either case, we have the option to perform any action we want
	in response to an event.  In our case, we look for our application user
	having pressed the F11 or F12 function key.

	When we detect an F12 key press, we make the application window
	cover the entire screen, remove all the 'decorations', and make it
	the top-most window on the screen.

	When we detect an F11 key press, we put all the 'decorations' back,
	make the window occupy the upper left corner of the screen, and
	remove the 'top-most' property.

	Any event other than F11 and F12 key presses gets forwarded to our
	application's message-processing function without intervention.
*/
LRESULT CALLBACK SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
	TCHAR charCode;
	
	switch(uMsg){
		case WM_KEYDOWN:
			charCode=(TCHAR)wParam;
			switch(charCode){
				case VK_F12:
					CoverScreen(hwnd);

					return FALSE;
				case VK_F11:
					UncoverScreen(hwnd);

					return FALSE;
				default:

					break;
			}

			break;
		default:

			break;

	}

	return CallWindowProc(subClassInfo.originalWndProc,
						  subClassInfo.windowOfInterest,
						  uMsg,
						  wParam,
						  lParam);
}

/*
	We subclass a window by instructing the window system to call a
	function we specify, instead of calling the application's original
	message-processing function, when a window event needs to be processed.
*/
BOOL SubclassAppWindow(struct SubClassInfo *sci, WNDPROC newProc){
	BOOL returnVal=SUCCESS;
	long result;

	result=SetWindowLong(sci->windowOfInterest, GWL_WNDPROC, (LONG) newProc);
	if(result){
		sci->originalWndProc=(WNDPROC)result;
		sci->windowIsSubclassed=TRUE;
	}
	else{
		returnVal=FAILURE;
	}
	
	return returnVal;
}

/*
	This function makes the associated window cover the entire computer
	screen, turns off all the 'decorations', and sets the window's top-most
	property.  The top-most property prevents other windows from showing
	on top of our application window.

	Removing the 'decorations' involves turning off the window's style bits
	governing certain window characteristics.  We remove the caption bar, which
	houses the minimize, maximize, and size boxes, as well as the system menu.
	We also turn off the thick frame borders, which house the window sizing
	handles.

	The effect is that is is very difficult to move the window or switch away
	from the application after calling this function.
*/
BOOL CoverScreen(HWND aWindow){
	SetWindowLong(aWindow, GWL_STYLE, GetWindowLong(aWindow, GWL_STYLE)
				  & ~(STYLE_BITS));
	SetWindowPos(aWindow, HWND_TOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN),
				 GetSystemMetrics(SM_CYSCREEN), 0 /* flags */);

	return SUCCESS;
}

/*
	This function restores the original 'decorations' to your application's
	window.  We move the window to the upper left corner of the screen and
	remove the top-most property.
*/
BOOL UncoverScreen(HWND aWindow){
	SetWindowLong(aWindow, GWL_STYLE, GetWindowLong(aWindow, GWL_STYLE)
				  | (STYLE_BITS));
	SetWindowPos(aWindow, HWND_NOTOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN)/2,
				 GetSystemMetrics(SM_CYSCREEN)/2, 0 /* flags */);

	return SUCCESS;
}

/*
	$Log: subClass.c $
	Revision 1.3  1997/03/13 15:49:42  doomer
	a fully functional window subclassing DLL.
	Revision 1.2  1997/03/13 11:30:40  doomer
	functional window subclassing.
	DllMain subclasses calling application's window when process attaches
	dll, un-subclasses when process detaches.
	The subclass procedure looks for wm_keydown messages and grabs f12.
	Revision 1.1  1997/03/12 17:50:32  doomer
	Initial revision
*/