There are times when a manufacturing test application that normally runs in a windowed environment would like to have sole ownership of the windowed desktop. There are a number of reasons for wanting this capability, but probably the most common is the need for preventing a test application from stalling while it is testing systems as part of a manufacturing process. An inadvertant key stroke could post a menu, bring another application to the top of the window stack, or in some other way cause the manufacturing-test application to stall.
This article presents a technique for allowing a single application to take sole ownership of the windows desktop. The intent is to allow you to prevent inadvertant interruptions in processing. It is extremely difficult to prevent a malicious user from causing harm if that person has physical access to a workstation. Although the manufacturing test application we use to illustrate this capability is HP VEE, you can apply the concepts presented in this article to any windows application.
The remainder of this article will present the implemenataion of a framework that can be used to allow sole desktop ownership. This framework is compatible with Windows 95™ and Windows NT™ 4.0. The application source was built with Microsoft Visual C++ 4.2.
There are several aspects of this framework which, when taken together, allow you to have one application take sole desktop ownership. Some of the implementation involves system configuration, while the bulk requires writing an application and a DLL.
The first of these is the need to replace the default Task
Manager and involves creating a substitute that the system
will load automatically. The need for replacing the task manager
arises, because the use of the default task manager prevents an
application from installing a certain set of window-navigation
hot keys. The replacement task manager's main responsibility is
to prevent application switching that would normally be exposed
by hot key combinations like
The second aspect of this framework involves an application owning the desktop. This involves changing the application window's behavior so that it prevents user interaction with any other desktop element. We accomplish this by making the application window:
Much of what we will discuss in regard to implementing the ability to have an application own the desktop was presented in Reference 3. View the article. |
The replacement task manager is a pretty vanilla windows application that we have named switcher. Its most important reponsibility is the capturing of hot keys that would normally allow you to switch between applications, etc. The intent is to disable features of the window system that let you get access to applications other than the one you are currently working with. The window system implements task switching, etc. by installing hot keys. The defualt task manager installs hot keys for the purposes of desk top navigation. Once a window is granted hot keys, no other application can ask to be the recipient of those hot key messages. There are some desk top navagation hot key sequences that applications are allowed to install hot key combinations for. So, our replacement task manager exists to replace the desk top navigation hot key actions that an application can't override with with actions that do nothing, thereby disabling the desk top navigation features that are outside an application's purview.
The following code fragment is taken from switcher's window-message-processing function and illustrates the steps required to have a window notify the window system that it wishes to be notified when the user enters a given hot-key combination.
static ATOM ctrlEscAtom, ctrlSpaceAtom, altEscAtom; ctrlEscAtom=GlobalAddAtom("veeCtrlEsc"); if(ctrlEscAtom){ if(RegisterHotKey(hWnd, ctrlEscAtom, MOD_CONTROL, VK_ESCAPE)){ ctrlEscRegistered=1; } } ctrlSpaceAtom=GlobalAddAtom("veeCtrlSpace"); if(ctrlSpaceAtom){ if(RegisterHotKey(hWnd, ctrlSpaceAtom, MOD_CONTROL, VK_SPACE)){ ctrlSpaceRegistered=1; } } altEscAtom=GlobalAddAtom("veeAltEsc"); if(altEscAtom){ if(RegisterHotKey(hWnd, altEscAtom, MOD_ALT, VK_ESCAPE)){ altEscRegistered=1; } } |
We perform the preceding steps when we receive the WM_CREATE window message; sent when our window is being constructed but before it is to be painted on the screen.
For each hot key you want to install, you must create an atom that allows the window system to identify the particular key sequence you are interested in. An atom is an integral value that speeds the process of looking up values stored in a table. The value you are interested in when you create an atom to identify a hot-key combination is a string. Looking up a string in a table when the window system wants to see if a particular sequence of key strokes corresponds to a sequence that has been registered as hot can be a time-consuming affair. So instead, you provide the operating system an index into a table of strings: an atom. The lookup, because it is based on an integral comparison, is fairly quick.
With atom in hand, you may now tell the window system that, when it sees a particular sequence of key strokes, it should send you a window message telling you about it. This is accomplished through the use of the RegisterHotKey Win32 ™ API function. You provide this function, along with the atom you created, the key combination you are interested in. That combination consists of a modifier key, like the control or alt key and another key stroke identifier. Table 1 lists the modifier/key stroke pairs that switcher registers as hot keys. Later on, we will discuss the use of hot keys from an application perspective.
Modifier | Key Stroke | Effect |
---|---|---|
Control | Escape | Brings up the Start menu. |
Control | Space | Selects or unselects desk top items. |
Alt | Escape | Cycles window stacking order. |
When you press a hot key combination, the window system sends a WM_HOTKEY message to the applicaton which has registered the hot key. In our case, we simply discard the message, effectively disabling the hot key.
You can permanently install our replacement task manager by creating a new (or editing an existing) registry entry. This entry is a string value containing the full path to the executable which will act as the new task manager. When the window system starts, it looks for the presence of this registry entry and will install the associated application in place of the default task manager. The entry you need to replace is located under the registry key:
HKEY_LOCAL_MACHINE\ SOFTWARE\ Microsoft\ Windows NT\ CurrentVersion\ Winlogon\ Taskman
One important note here is that this registry entry gets picked up at boot time only by versions of Windows NT numbered 3.5 and higher.
Figure 1 is a screen shot of the Windows NT ™ regestry editor showing the value the author used to test the configuration presented in this article.
![]() |
The second part of the framework is to give the target application the ability to consume the entire windows desktop, to prevent resizing the application window, and prevent an application not part of the framework from obscuring the application window. There are several ways to accomplish this. The technique we present in this article relies on the application's ability to load a DLL. We discuss (though we do not implement) one alternative that uses a launcher application to control the target application's window characteristics.
Figure 2 illustrates the sequence of events that permit an application to own the desktop.
Figure 2 is a screen shot of a VEE program that loads the SecureVee dll and calls functions to demonstrate the ability to own the desktop. The functions of primary interest are SecureVee and SecureVeeCleanup. SecureVee is the function that makes the calling application take over the desktop; SecureVeeCleanup is the function that puts the desktop back the way it was before an application called the SecureVee function.
The program first loads the SecureVee dll using the Import Library object. Next it calls the SecureVee function, taking over the desktop. If the call to the SecureVee function is successful, as indicated by the use of the If/Then/Else object, then the program enables an OK button. The program will pause on the OK button until you click it with the mose. Once the OK button runs, the program calls the SecureVeeCleanup function, which restores the application to the state it was in before you took control of the desktop.
![]() |
Using the SecureVee DLL from Visual Basic is relatively similar to using it from VEE. Your Basic application first attaches the DLL and makes provisions for calling the exported functions. Figure 4 illustrates the way we attached the DLL and exposed its capabilities in our form.
![]() |
We added two command buttons to the form to illustrate calling the functions in our DLL. The button labeled Cover calls the SecureVee function when pressed, while the button labeled Uncover calls the SecureVeeCleanup function. Figure 5 contains the listings showing the click callback functions for each of the two command buttons.
Taking Control |
![]() |
Releasing Control |
![]() |
It is from the SecureVee function that all our capability to own the desktop derives. One point worth mentioning here is that any DLL function that you wish to have called from a Visual Basic program must be declared using the __stdcall calling convention. A calling convention specifies how function arguments and return values are placed on the call stack. The code calling the function and the function itself must agree on this convention. The listing follows:
int __stdcall SecureVee(void) { HWND veeWindow; int returnVal=1; BOOL hotKeyResult; BOOL subclassResult; BOOL decorationsResult; veeWindow=findVeeWindow(); if (!veeWindow) { return(0); } mainVeeWindow=veeWindow; hotKeyResult=createHotKey(&hotKeyId, veeWindow); if (!hotKeyResult) { returnVal=0; } subclassResult=subclassVeeWindow(&subclassId, veeWindow); if (!subclassResult) { returnVal=0; } decorationsResult=removeDecorations(&subclassId, veeWindow); if (!decorationsResult) { returnVal=0; } return(returnVal); } |
One of the capabilities our DLL needs to provide is a mechanism to unambiguously identify our progenetor window. When a function in a DLL gets called, it performs its actions in the context of a thread. That thread will also have created a window prior to having called the SecureVee function. There is the potential that there is more than one copy of an application running, so there would be more than one window that could answer to the same name as the one we're actually interested in. To unambiguously identify the window on whose behalf we wish to work, we must associate our thread identifier with the top-level window this thread created. Two functions, taken as a cooperating pair, deliver this capability. Listing 3 details the functions which identify our window:
HWND findVeeWindow(void) { struct windowThreadPair windowThreadInfo; BOOL result; windowThreadInfo.aWindow=(HWND)0; windowThreadInfo.windowThread=GetCurrentThreadId(); result=EnumThreadWindows(windowThreadInfo.windowThread, enumWindowsCallback, (LPARAM)&windowThreadInfo); return(windowThreadInfo.aWindow); } BOOL CALLBACK enumWindowsCallback(HWND aWnd, LPARAM windowThreadInfo) { DWORD windowThreadId; struct windowThreadPair *windowPairInfo; windowPairInfo=(struct windowThreadPair *)windowThreadInfo; windowThreadId=GetWindowThreadProcessId(aWnd, (LPDWORD)0); if (windowThreadId == (DWORD)windowPairInfo->windowThread) { windowPairInfo->aWindow=aWnd; return(FALSE); } return(TRUE); } |
What we are in search of is the top-level window (the application's main window) created by the thread on whose behalf this DLL is working. To do this, we create two functions which, working in tandem, uniquely identify our window. The first function is findVeeWindow, which accomplishes its task through the use of the function EnumThreadWindows. EnumThreadWindows takes three arguments:
Identifying the currently-executing thread is easy: you just call GetCurrentThreadId. Our enumeration function is enumWindowsCallback. We'll explain how it fits in the process of identifying our window shortly. The data we will pass to our callback function is a pointer to a sructure that associates a window with the current thread identifier. The structure definition is contained in Listing 4.
struct windowThreadPair{ HWND aWindow; DWORD windowThread; }; |
When you call EnumThreadWindows, the window system will repeatedly call your callback function, supplying as an argument one of the top-level windows currently on the desktop. The window system will continue to call your function until it has cycled through all the windows or until the callback function indicates that it wants to halt the window enumeration process.
In our enumWindowsCallback function, we make a call to GetWindowThreadProcessId. This tells us which thread created the window the window system has currently passed as an argument to our callback. If that thread identifier matches the thread identifier we got in the call to GetCurrentThreadId, then we have found our application's top-level window. At this point, we inform the window system that we wish to discontinue the window enumeration by having the callback function return the manifest constant FALSE.
Once we have our window handle, we have enough information to implement the remainder of our architecture. First, we register several hot keys that would normally give the interactive user the capability to navigate through the desktop using the keyboard. We disable that navigation using the code in Listing 5.
BOOL createHotKey(struct windowAtomPair *hotKeyInfo, HWND aWnd) { hotKeyInfo->aWindow=aWnd; return(installHotKey(hotKeyInfo)); } BOOL installHotKey(struct windowAtomPair *hotKeyInfo) { char altTabAtomString[MAX_STRING_SIZE + 1]; char altF4AtomString[MAX_STRING_SIZE + 1]; char altReturnAtomString[MAX_STRING_SIZE + 1]; char altEscAtomString[MAX_STRING_SIZE + 1]; char altSpaceAtomString[MAX_STRING_SIZE + 1]; HMODULE thisModule; thisModule=GetModuleHandle("SecureVee"); if(!LoadString(thisModule, IDS_ALT_TAB_ATOM, altTabAtomString, MAX_STRING_SIZE)){ return(0); } if(!LoadString(thisModule, IDS_ALT_F4_ATOM, altF4AtomString, MAX_STRING_SIZE)){ return(0); } if(!LoadString(thisModule, IDS_ALT_RETURN_ATOM, altReturnAtomString, MAX_STRING_SIZE)){ return(0); } if(!LoadString(thisModule, IDS_ALT_ESC_ATOM, altEscAtomString, MAX_STRING_SIZE)){ return(0); } if(!LoadString(thisModule, IDS_ALT_SPACE_ATOM, altSpaceAtomString, MAX_STRING_SIZE)){ return(0); } hotKeyInfo->altTabAtom=GlobalAddAtom(altTabAtomString); if (!hotKeyInfo->altTabAtom) { return(0); } hotKeyInfo->flags.altTabAtom=1; hotKeyInfo->altF4Atom=GlobalAddAtom(altF4AtomString); if (!hotKeyInfo->altF4Atom) { return(0); } hotKeyInfo->flags.altF4Atom=1; hotKeyInfo->altReturnAtom=GlobalAddAtom(altReturnAtomString); if (!hotKeyInfo->altReturnAtom) { return(0); } hotKeyInfo->flags.altReturnAtom=1; hotKeyInfo->altEscAtom=GlobalAddAtom(altEscAtomString); if (!hotKeyInfo->altEscAtom) { return(0); } hotKeyInfo->flags.altEscAtom=1; hotKeyInfo->altSpaceAtom=GlobalAddAtom(altSpaceAtomString); if (!hotKeyInfo->altSpaceAtom) { return(0); } hotKeyInfo->flags.altSpaceAtom=1; if(!RegisterHotKey(hotKeyInfo->aWindow, hotKeyInfo->altTabAtom, MOD_ALT, VK_TAB)) { return(0); } hotKeyInfo->flags.altTabInstalled=1; if(!RegisterHotKey(hotKeyInfo->aWindow, hotKeyInfo->altF4Atom, MOD_ALT, VK_F4)) { return(0); } hotKeyInfo->flags.altF4Installed=1; if(!RegisterHotKey(hotKeyInfo->aWindow, hotKeyInfo->altReturnAtom, MOD_ALT, VK_RETURN)) { return(0); } hotKeyInfo->flags.altReturnInstalled=1; if(!RegisterHotKey(hotKeyInfo->aWindow, hotKeyInfo->altEscAtom, MOD_ALT, VK_ESCAPE)) { return(0); } hotKeyInfo->flags.altEscInstalled=1; if(!RegisterHotKey(hotKeyInfo->aWindow, hotKeyInfo->altSpaceAtom, MOD_ALT, VK_SPACE)) { return(0); } hotKeyInfo->flags.altSpaceInstalled=1; return(1); } |
Creating hot keys from within the DLL is no different than the process we described when discussing the replacement task manager. We disable:
Notice that nowhere did we try to attempt to disable the
Please see Reference 3 for a focused discussion of window subclassing. |
The next step in the process of owning the desktop is to subclass the application's event-handling function. Window subclassing is a technique that enables an application or DLL to alter the behavior of another window. When you create a window, one of the pieces of information you supply to the window system is the name of a function you wish to be called when the window system needs to inform your window that an event has take place. We will call this function our event handler. The way the event handler responds to events defines the actions an application will take in response to any window event. For instance, if the application user selects the File->Exit entry on the application's menu, the event handler would normally do whatever it takes to close the application.
To subclass a window, you tell the window system to call an event handler other than the one specified when the application created its window. The new event handler can respond to events in any manner it sees fit. It can disregard an event, effectivley disabling it. Listing 6 is the function we define to replace the application's original event handler.
BOOL subclassVeeWindow(struct windowSubclassPair *subclassInfo, HWND aWnd) { subclassInfo->windowPlacement.length = sizeof(subclassInfo->windowPlacement); if(GetWindowPlacement(aWnd, &subclassInfo->windowPlacement)){ subclassInfo->flags.windowPlacementValid = 1; } subclassInfo->aWnd=aWnd; subclassInfo->winProc=SubclassWindow(aWnd, veeSubclass); subclassInfo->flags.windowIsSubclassed=1; return(1); } |
SubclassWindow is a macro API that calls SetWindowLong. Every window has some associated data structures that help the window system define behavior. The event handler is stored in one of these structures. SetWindowLong is a function that replaces the contents of a structure. By using it, we instruct the window system to use the event handler we define, rather than the one the application defined.
The replacement event handler is contained in Listing 7.
LRESULT CALLBACK veeSubclass( HWND aWnd, UINT aMessage, WPARAM wParam, LPARAM lParam) { switch (aMessage) { case WM_HOTKEY: return(0); case WM_CHAR: if(wParam == VEE_CTRL_E){ return(0); } break; case WM_COMMAND: switch(LOWORD(wParam)){ case VEE_FILE_EXIT: return(0); default: break; } break; default: break; } return(CallWindowProc(subclassId.winProc, aWnd, aMessage, wParam, lParam)); } |
We respond to hot-key events by doing nothing, effectively preventing keyboard-initiated window navigation.
We also catch the WM_COMMAND message. This is the message the window system sends when the application user interacts with the menu. The system sends along an identifier telling us which menu entry was selected. If the the identifier corresponds to that which the File->Exit entry generates, we discard it, disabling that menu entry. All other menu selections get through to the application.
We found the File->Exit menu identifier by setting a breakpoint in our DLL. When we broke at the line that looks for WM_COMMAND messages, we recorded the menu entry identifier. This identifier may change from one application revision to another, so you will want to check that the value you recieve in your DLL is actually the entry identifier you had intended.
We also catch WM_CHAR messages. We do this to disable
the
Any events we aren't interested in processing get forwarded to the original event handler. We saved a pointer to the original event handling function when we subclassed the window. To call it, we use the CallWindowProc function.
So, now that we have the application to the point that it has effectively disabled keyboard-intitiated window navigation, it is time to cover up the screen, so that the only access to the desktop is from our application. We accomplish this by removing all the window's controls and decorations, sizing the window, so that it covers the entire visible area, then setting the always-on-top property.
When you create a window, one of the pieces of information you supply is a set of style bits that tells the window system what controls the window will have. The controls (commonly called decorations, because they're not part of the window itself) include things like the resizing borders, minimize, maximize, and close buttons, and the system menu. By changing the contents of a window's style bits, you inform the window system that you want to present different capabilities to the application user. You change window style bits using the function SetWindowLong and query for them using the function GetWindowLong. The style bits we will turn off include:
Listing 8 is the function we use to remove window decorations.
BOOL removeDecorations(struct windowSubclassPair *subclassInfo, HWND aWnd) { long windowDecorations, newDecorations; windowDecorations=GetWindowLong(aWnd, GWL_STYLE); if(windowDecorations){ subclassInfo->flags.stylesValid = 1; subclassInfo->styleBits = windowDecorations; } newDecorations = ~(WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_THICKFRAME | WS_OVERLAPPED); windowDecorations &= newDecorations; SetWindowLong(aWnd, GWL_STYLE, windowDecorations); /* Comment this next block out when debugging. If you don't you won't be able to see the debugger when it hits a break point. Then it's three-finger salute time. */ return(coverScreen(aWnd, 1 /* top-most */)); } |
Notice that the last line in Listing 8 is a call to the function coverScreen. This function finds the size of the visible desktop, sets the application window size to the desktop dimensions, then sets the always-on-top property.
Listing 9 is the function we defined to have an applicaton window cover the desktop. We get the desktop dimensions using calls to the GetSystemMetrics function. We the move the application window to the top left corner of the screen and make it fill the entire desktp using a call to the SetWindowPos function. Specifying the HWND_TOPMOST flag tells the window system to prevent any other window not also marked with the always-on-top property from obscuring our application window.
BOOL coverScreen(HWND aWnd, BOOL lockPosition) { int screenWidth; int screenHeight; screenWidth=GetSystemMetrics(SM_CXSCREEN); screenHeight=GetSystemMetrics(SM_CYSCREEN); if (lockPosition) { return(SetWindowPos(aWnd, HWND_TOPMOST, 0, 0, screenWidth, screenHeight, SWP_SHOWWINDOW)); } return(SetWindowPos(aWnd, 0, 0, 0, screenWidth, screenHeight, 0)); } |
Newtonian physics tells us that, for every action, there must be an equal and opposite reation. So too must there be a way to undo all the damage we just did. The function SecureVeeCleanup does just that. When we subclassed the application's window and removed its decorations, we saved off the information necessary to restore it to its original state. SecureVeeCleanup uses this saved information and is called either explicitly from our application or from our DLL's main entry point at the time our application unloads the DLL. Listing 11 illustrates the way SecureVeeCleanup uses the information stored in the windowSubclassPair structure to restore our application window's original state. Listing 10 details the data structures used to encapsulate the application window's original state.
SecureVeeCleanup uses two helper functions to manage the window restoration: cleanup and restoreWindow. cleanup provides the implementation of SecureVeeCleanup. You might wonder why we have two functions; one of which appears to be nothing other than a call to a function that does what you think the first one should do. The answer to the mystery is that cleanup uses a set of data structures whose types are unknown to languages other than C or C++. The function signature of SecureVeeCleanup is simple and can be called form most any language that can call a DLL.
cleanup's main responsibility is to remove all the hot keys we installed. restoreWindow puts the application's window back the way we found it. We put back the original event handler function by using a second call to SubclassWindow then retore the window decorations using a call to SetWindowLong. The call to SetWindowPlacement balances the call to GetWindowPlacement (made when we subclassed the window). This function restores the window location. We then make a call to SetWindowPos to turn off the window's top-most property.
Note that we reset the elements of the encapsulated flags field. This is necessary to prevent two successive calls to our cleanup function (as would happen if the application were to call SecureVeeCleanup explicitly, then unload the DLL) from performing actions which would make the window jump around.
/* information needed to know whether we can restore our window to its original state; */ struct subclassFlags{ unsigned char windowIsSubclassed :1; unsigned char windowPlacementValid :1; unsigned char stylesValid :1; }; /* associate our window with its original event handler for restoration when we exit */ struct windowSubclassPair{ HWND aWnd; WNDPROC winProc; WINDOWPLACEMENT windowPlacement; LONG styleBits; struct subclassFlags flags; }; |
void __stdcall SecureVeeCleanup(void) { cleanUp(&hotKeyId, &subclassId); return; } void cleanUp( struct windowAtomPair *hotKeyInfo, struct windowSubclassPair *subclassInfo){ BOOL unregisterResult; ATOM deleteAtomResult; if (hotKeyInfo->flags.altTabInstalled) { unregisterResult=UnregisterHotKey(hotKeyInfo->aWindow, (int)hotKeyInfo->altTabAtom); hotKeyInfo->flags.altTabInstalled=0; } if (hotKeyInfo->flags.altF4Installed) { unregisterResult=UnregisterHotKey(hotKeyInfo->aWindow, (int)hotKeyInfo->altF4Atom); hotKeyInfo->flags.altF4Installed=0; } if (hotKeyInfo->flags.altReturnInstalled) { unregisterResult=UnregisterHotKey(hotKeyInfo->aWindow, (int)hotKeyInfo->altReturnAtom); hotKeyInfo->flags.altReturnInstalled=0; } if (hotKeyInfo->flags.altEscInstalled) { unregisterResult=UnregisterHotKey(hotKeyInfo->aWindow, (int)hotKeyInfo->altEscAtom); hotKeyInfo->flags.altEscInstalled=0; } if (hotKeyInfo->flags.altSpaceInstalled) { unregisterResult=UnregisterHotKey(hotKeyInfo->aWindow, (int)hotKeyInfo->altSpaceAtom); hotKeyInfo->flags.altSpaceInstalled=0; } if (hotKeyInfo->flags.altTabAtom) { deleteAtomResult=GlobalDeleteAtom(hotKeyInfo->altTabAtom); hotKeyInfo->flags.altTabAtom=0; hotKeyInfo->altTabAtom=(ATOM)0; } if (hotKeyInfo->flags.altF4Atom) { deleteAtomResult=GlobalDeleteAtom(hotKeyInfo->altF4Atom); hotKeyInfo->flags.altF4Atom=0; hotKeyInfo->altF4Atom=(ATOM)0; } if (hotKeyInfo->flags.altReturnAtom) { deleteAtomResult=GlobalDeleteAtom(hotKeyInfo->altReturnAtom); hotKeyInfo->flags.altReturnAtom=0; hotKeyInfo->altReturnAtom=(ATOM)0; } if (hotKeyInfo->flags.altEscAtom) { deleteAtomResult=GlobalDeleteAtom(hotKeyInfo->altEscAtom); hotKeyInfo->flags.altEscAtom=0; hotKeyInfo->altEscAtom=(ATOM)0; } if (hotKeyInfo->flags.altSpaceAtom) { deleteAtomResult=GlobalDeleteAtom(hotKeyInfo->altSpaceAtom); hotKeyInfo->flags.altSpaceAtom=0; hotKeyInfo->altSpaceAtom=(ATOM)0; } restoreWindow(subclassInfo); return; } /* Put the window back the way we found it. */ BOOL restoreWindow(struct windowSubclassPair *subclassInfo){ BOOL result = 1; if (subclassInfo->flags.windowIsSubclassed) { SubclassWindow(subclassInfo->aWnd, subclassInfo->winProc); subclassInfo->winProc=(WNDPROC)0; subclassInfo->flags.windowIsSubclassed=0; } if(subclassInfo->flags.stylesValid){ if(!SetWindowLong(subclassInfo->aWnd, GWL_STYLE, subclassInfo->styleBits)){ result = 0; } subclassInfo->flags.stylesValid = 0; } if(subclassInfo->flags.windowPlacementValid){ if(!SetWindowPlacement(subclassInfo->aWnd, &subclassInfo->windowPlacement)){ result = 0; } /* Remove the 'top-most' flag. Setting the 'nosize' and 'nomove' flags in the call to SetWindowPos() tells this function to ignore the widht, height, x position, and y position arguments. */ else{ if(!SetWindowPos(subclassInfo->aWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOREDRAW | SWP_NOSIZE | SWP_NOMOVE)){ result = 0; } } subclassInfo->flags.windowPlacementValid = 0; } return result; } |
I chose to implement this framework using a replacement task manager and an application-level DLL to give the application programmer the most flexibility over the environment. Using this framework, he application developer is free to choose when to enable and disable access to he window system. There are alternatives that may be more applicable to a given situation.
Our example relies on having an application that is capable of calling functions in a DLL. This may not always be the case. One alternative would be to call the window-system-restriction function from the DLL's entry point when the DLL is loaded into an application, then restore window system access when the DLL is detached. The DLL will be detached when the application shuts down. This approach will work when using an application capable of calling a function in a DLL, but is particularly useful in the case of an application that can't. If you can inject the DLL into any application's process space, the application which is the recipient of the injection will restrict access to the window system, by virtue of the fact that the DLL entry point will run when the DLL is pushed into the application's process space. Reference 2 lists an article which will detail the process of injecting a DLL into an application's process space. Use a Small Launcher Application to Spawn VEE
Instead of launching VEE directly from the Startup group consider the use of a small launcher application. That application might simply call the CreateProcess function, then wait for the application process to exit. At target process exit, your launcher application could then restart the window system.
As we have it now, the replacement task manager restricts a small number of the window-navigation constructs that would normally be available. Because the replacement task manager is loaded when the system boots, it affects every system user for that session. Having the remainder of the window-navigation restrictions take place from an application gives you flexibility in deciding which category of users you want to have the most severe restictions. This architecture can be complex, though. One way to make the system less complex, at the expense of limiting window system access to everyone, would be to place all the hot-key definitions in the replacement task manager.
You may feel free to use the source files presented in thius atricle in any manner you see fit, so long as you adhere to two simple rules:
Implementation File
Header File
Header File Listing Resource Identifiers
Resource Definition File
Linker Definition File
Make File
Implementation File
Visual C++ 4.2 Make File
Demonstration Program
Import Library Definition File
Visual Basic Project
Visual Basic Form