[ This app is now on github !! ]
This is a small program I wrote to hide/show different windows on my Windows PC.
This is named LifeSaver
because it did save my life several times, whenever someone stormed into my room unexpectedly. It does have some cool uses and some naughty uses, for all people.
Its structure is like a stack. You activate a window, and then press LCtrl+F7. The window will be captured and pushed into the stack. I call these the slots. There are 10 empty slots in the beginning. Slots are numbered from 0 to 9. So every captured window goes into the next available slot. When captured, the slot number is briefly flashed on the window. Popping from the stack is currently not allowed. However, clearing the whole stack is possible. Just press LCtrl+F8 to clear the stack, and start over again.
To flash the slot number, I used GDI (not GDI+). Painting the digit is easy, but erasing is impossible. I did what I could do to make the window repaint itself and hence erase the digit. I hope it is okay-ish.
To interact with the program, the LCtrl button must be pressed. Then there are variety of controls to manipulate the windows. They can be toggled between hidden/shown individually by pressing LCtrl+N, where N is the slot number the window was captured into and was flashed briefly on the screen. N must be pressed from the numeric pad.
By pressing LCtrl+LWin, all the visible captured windows can be toggled to hidden simultaneously. This is referred to as "Special Hide" in the source code. It is special because when LCtrl+LWin is pressed again, it toggles the windows back to visible, but only specifically the ones which were hidden with "Special Hide". This is referred to as, appropriately enough, "Special Show" in the source code.
If in emergency, by pressing LCtrl+RCtrl keys together, you can to kill all captured windows (whether currently visible or hidden) and followed by this program killing itself. To kill the windows, I send them WM_QUIT
and WM_CLOSE
both, and hope they will respond and terminate. As of now, this method is working fine. But please suggest a more robust way of killing those.
The input loop is a little confusing. I didn't use much message based processing. Instead I used GetAsyncKeyState()
and then added some loop trickery to eliminate input bounces. The loop checks if a certain key combination is pressed every 0.1 seconds. If so, then appropriate action is performed. Some flags are used to eliminate things like bounces, distinct double input, etc. It's messy, but works. And I didn't know about source control back then.
Instead of word-wrapping, copy/paste it into some editor and view in widescreen:
//////////////////////////////////////////////////////////////////
//// Programmer Info
//////////////////////////////////////////////////////////////////
// Name: Anees Ahmed
// eMail: [email protected]
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//// Application Info
//////////////////////////////////////////////////////////////////////
// Name: LifeSaver
// Version: 3.0
// Completed: 4 June 2015
// Purpose: Hide specific windows from the screen whenever wanted
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//// How To Use (Assuming Default Controls)
/*////////////////////////////////////////////////////////////////////////////////////////////
You can manipulate only captured windows
1. To capture a window, activate it and press LCtrl+F7.
The windows are arranged into slots...
The window which was captured first goes into Slot 0.
The window which was captured second goes into Slot 1.
The window which was captured third goes into Slot 2.
Ando so on upto a maximum of Slot 9. (Total 10 windows)
For your Convenience, the Slot No. will flash briefly on the window you just captured.
2. To Uncapture all windows at once, press LCtrl+F8.
It will clear all the slots.
3. To Toggle the visibility of the window in Slot N, press LCtrl+N.
(The N corresponding a Number key on the Numeric Keypad)
4. To temporarily toggle the visibilty of all visible captured windows, press LCtrl+LWin.
This is referred to as SpecialHide/SpecialShow.
5. To Exit without caring about the captured windows, press LCtrl+RShift.
There is no way to Unhide the windows once the application is quit.
6. To close all captured windows silently and exit, press LCtrl+RCtrl.
The method used by the application to close the windows may not work for some windows.
/*////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
//// Some defines for choosing Windows version
////////////////////////////////////////////////
#define Windows7 0x601
#define _WIN32_WINNT Windows7
#define WIN32_LEAN_AND_MEAN
////////////////////////////////////////////////
///////////////////////
//// Headers needed
///////////////////////
#include <windows.h>
#include <vector>
///////////////////////
//////////////////////////////////////////////////
//// This may be moved to a separate resource.h
//////////////////////////////////////////////////
#define ID_MY_TIMER 7777
//////////////////////////////////////////////////
// Forward Declare Windows procedure
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// Global Variables
////////////////////////////////////////////////////////////////////////////////////////////////////////////
char className[ ] = "GhostWinClass";
bool flag_OperationAllowed; // a flag which will be used to block repetitive key-press detections
bool flag_ShowSpecialHidden; // a flag to store whether to perform ShowSpecial or HideSpecial operation
struct WinData // a struct storing essential manipulation data about a captured window
{
HWND hTargetWindow; // Handle to the captured window
bool IsVisible; // a flag to store whether the window is currently visible or hidden
bool IsSpecialHidden; // a flag indicating the window was 'specially' hidden (by HideSpecial operation)
};
std::vector<WinData> winObjects; // A stack to store data about multiple captured windows
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//// I think I can add some extra layer of abstraction by declaring these...
//// (And the Controls can be easily remapped here)
////////////////////////////////////////////////////////////////////////////
#define HOLD_FOR_ACCESS GetAsyncKeyState(VK_LCONTROL)
#define CAPTURE_WINDOW GetAsyncKeyState(VK_F7)
#define UNCAPTURE_ALL GetAsyncKeyState(VK_F8)
//#define TOGGLE_WIN_X GetAsyncKeyState(VK_NUMPADX) //This control cannot be remapped, its a dummy line.
#define TOGGLE_SPECIAL GetAsyncKeyState(VK_LWIN)
#define DIE GetAsyncKeyState(VK_RSHIFT)
#define KILL_AND_DIE GetAsyncKeyState(VK_RCONTROL)
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//// The EntryPoint (WinMain)
//////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE /*hPrevInstance*/,
LPSTR /*lpszArgument*/,
int /*nCmdShow*/)
{
MSG messages; // Here messages to the application are saved
WNDCLASSEX winclass; // Data structure for the windowclass
// The Window structure
winclass.hInstance = hThisInstance;
winclass.lpszClassName = className;
winclass.lpfnWndProc = WindowProcedure;
winclass.style = CS_DBLCLKS;
winclass.cbSize = sizeof (WNDCLASSEX);
winclass.hIcon = LoadIcon (nullptr, IDI_APPLICATION);
winclass.hIconSm = LoadIcon (nullptr, IDI_APPLICATION);
winclass.hCursor = LoadCursor (nullptr, IDC_ARROW);
winclass.lpszMenuName = nullptr;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hbrBackground = (HBRUSH) GetStockObject(NULL_BRUSH);
// Register the window class, and if it fails quit the program
if (!RegisterClassEx (&winclass))
return 0;
// The class is registered, let's create a window
CreateWindowEx (
0, // Extended possibilites for variation
className,
"Win32", // Title Text
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, // Windows decides the position
CW_USEDEFAULT,
544, // Window width in pixels
375, // Window height in pixels
HWND_DESKTOP, // Parent Window
nullptr, // No menu
hThisInstance, // Program Instance handler
nullptr // No Window Creation data
);
//** The window is currently Not visible on the screen, and that's what we want. **//
// Run the message loop. It will run until GetMessage() returns 0...
while (GetMessage (&messages, nullptr, 0, 0))
{
// Translate virtual-key messages into character messages
//TranslateMessage(&messages); // Currently we do not need this... Why waste CPU ??
// Send message to WindowProcedure
DispatchMessage(&messages);
}
// Return the return-value that PostQuitMessage() gave
return messages.wParam;
}
///////////////////////////////////////////////////////////////////////////////
//// Some Functions
///////////////////////////////////////////////////////////////////////////////
void CreateWinData_then_Push (HWND hWin)
{
WinData temp_winObj;
temp_winObj.hTargetWindow = hWin;
temp_winObj.IsVisible = true;
winObjects.push_back(temp_winObj);
}
void Clear_Stack ()
{
winObjects.clear();
}
void HideWindow (WinData *winObj)
{
AnimateWindow(winObj->hTargetWindow, 1, AW_HIDE | AW_BLEND);
winObj->IsVisible = false;
}
void ShowWindow (WinData *winObj)
{
AnimateWindow(winObj->hTargetWindow, 1, AW_ACTIVATE | AW_BLEND);
winObj->IsVisible = true;
}
void HideWindowSpecial (WinData *winObj)
{
if (winObj->IsVisible)
{
HideWindow(winObj);
winObj->IsSpecialHidden = true;
}
else
{
winObj->IsSpecialHidden = false;
}
}
void ShowWindowSpecial (WinData *winObj)
{
if (winObj->IsSpecialHidden)
ShowWindow(winObj);
}
void FlashDigitOnWindow (HWND hTargetWnd, int digit, int duration)
{
HDC hDC = GetWindowDC(hTargetWnd);
const WCHAR wCh = L'0' + digit;
RECT rcClient;
GetClientRect(hTargetWnd, &rcClient);
int width = rcClient.right;
int height = rcClient.bottom;
int limiter = ( width < height ? width : height );
long charHeight = -MulDiv(limiter*90/100, GetDeviceCaps(hDC, LOGPIXELSY), 72);
HFONT hFont = CreateFont(charHeight, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, "Consolas");
SelectObject(hDC, hFont);
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, RGB(0,255,0));
DrawTextW(hDC, &wCh, 1, &rcClient, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
ReleaseDC(hTargetWnd, hDC);
Sleep(duration);
WINDOWPLACEMENT wndpl, wndpl_temp;
wndpl.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hTargetWnd, &wndpl);
wndpl_temp = wndpl;
wndpl_temp.showCmd = SW_INVALIDATE;
SetWindowPlacement(hTargetWnd, &wndpl_temp);
SetWindowPlacement(hTargetWnd, &wndpl);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//// The Window Procedure : called by the Windows function DispatchMessage()
///////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hSelf, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: // Init stuff...
SetTimer(hSelf, ID_MY_TIMER, 100, nullptr);
flag_OperationAllowed = true;
flag_ShowSpecialHidden = false;
return 0;
case WM_TIMER: // Do every 100ms...
if ( HOLD_FOR_ACCESS && flag_OperationAllowed )
{
if ( CAPTURE_WINDOW && winObjects.size() < 10 )
{
HWND hForegrndWnd = GetForegroundWindow();
// Create a WinData obj by extracting info about the foreground window and
// then Push it into the stack
CreateWinData_then_Push(hForegrndWnd);
// Now briefly show the assigned Slot No. on the Window for ease of use
FlashDigitOnWindow(hForegrndWnd, winObjects.size() -1, 500);
}
else if ( UNCAPTURE_ALL )
{
// Pop all items of the stack
Clear_Stack();
}
else if ( DIE )
{
// Close itself
SendMessage(hSelf, WM_CLOSE, 0, 0);
}
else if ( KILL_AND_DIE )
{
// Try to close all the windows...
for (unsigned int i = 0, N = winObjects.size(); i < N; ++i)
{
PostMessage(winObjects[i].hTargetWindow, WM_CLOSE, 0, 0);
PostMessage(winObjects[i].hTargetWindow, WM_QUIT, 0, 0);
PostMessage(winObjects[i].hTargetWindow, WM_CLOSE, 0, 0);
}
// Now close itself
SendMessage(hSelf, WM_CLOSE, 0, 0);
}
else if ( TOGGLE_SPECIAL )
{
if (flag_ShowSpecialHidden) // Check whether to perform ShowSpecial or HideSpecial operation
{
for (unsigned int i = 0, N = winObjects.size(); i < N; ++i)
ShowWindowSpecial(&winObjects[i]);
flag_ShowSpecialHidden = false; // Flip the Flag
}
else
{
for (unsigned int i = 0, N = winObjects.size(); i < N; ++i)
HideWindowSpecial(&winObjects[i]);
flag_ShowSpecialHidden = true; // Flip the Flag
}
}
else
{
for (unsigned int numpadkey = 0; numpadkey <= 9; ++numpadkey) // Check all Numpad Numeric Keys
if ( GetAsyncKeyState(numpadkey + VK_NUMPAD0) ) // If a Numpad Number Key is pressed...
{
// Cancel if no window captured at current slot
if ( winObjects.size() < numpadkey +1 )
break;
// Toggle the visibility status of the window in current slot
if ( winObjects[numpadkey].IsVisible )
HideWindow(&winObjects[numpadkey]);
else
ShowWindow(&winObjects[numpadkey]);
break; // No need to check any further numpad keys...
}
}
}
// Initially set the flag to True...
flag_OperationAllowed = true;
// But set it to false if any control keys are pressed...
if ( CAPTURE_WINDOW || UNCAPTURE_ALL || TOGGLE_SPECIAL )
flag_OperationAllowed = false;
for (unsigned int numpadkey = 0; numpadkey <= 9; ++numpadkey)
if ( GetAsyncKeyState(numpadkey + VK_NUMPAD0) )
{
flag_OperationAllowed = false;
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage (0);
return 0;
default:
return DefWindowProc (hSelf, message, wParam, lParam);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//////////////
comments can be a pain to maintain all over the project \$\endgroup\$/* */
), and having to type so many/////
in a row makes them even distractive (though this last part could be debated) \$\endgroup\$