4
\$\begingroup\$

[ 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);
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////
\$\endgroup\$
7
  • \$\begingroup\$ One thing I can assure you: these ////////////// comments can be a pain to maintain all over the project \$\endgroup\$
    – streppel
    Feb 3, 2016 at 17:26
  • \$\begingroup\$ @amigo Why ? (just out of curiosity) \$\endgroup\$ Feb 4, 2016 at 8:32
  • \$\begingroup\$ I mean, that's what multiple line comments are for (/* */), and having to type so many ///// in a row makes them even distractive (though this last part could be debated) \$\endgroup\$
    – streppel
    Feb 4, 2016 at 16:38
  • \$\begingroup\$ @amigo Actually, I just did it to partition the segments better. They are looking nice from my point of view. Can you suggest examples of less distractive ways to put information in the source ?? \$\endgroup\$ Feb 5, 2016 at 8:56
  • 1
    \$\begingroup\$ @AneesAhmed777 We upvote because we think the question is good and should get reviewed. But maybe we don't have any points of review ourselves, or don't know enough about the language. Hence, no review. Personally, I know a bit of C++, but not enough to give you a review of the parts you really want reviewed... I'll try, though. \$\endgroup\$
    – Pimgd
    Feb 12, 2016 at 9:08

2 Answers 2

2
\$\begingroup\$

So I spotted this bit...

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
}

If all you need to do is "flip the flag" at the end, you can do so by negating the current value:

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]);
}
else
{
    for (unsigned int i = 0, N = winObjects.size(); i < N; ++i)
        HideWindowSpecial(&winObjects[i]);
}
flag_ShowSpecialHidden = !flag_ShowSpecialHidden;

Then you have this:

        // 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;
            }

Now, I don't know if GetAsyncKeyState has any sideeffects like clearing input buffers, but if all you're gonna do is set flag_OperationAllowed, then clearly we don't need to enter this loop if flag_OperationAllowed is already false. And setting it to true only it set it to false some time later in the same section of code is a waste of cycles.

So do it like this:

// Initially set the flag to True...
flag_OperationAllowed = !(CAPTURE_WINDOW || UNCAPTURE_ALL || TOGGLE_SPECIAL )
if ( flag_OperationAllowed ) { //check numpadkeys
    for (unsigned int numpadkey = 0; numpadkey <= 9; ++numpadkey)
        if ( GetAsyncKeyState(numpadkey + VK_NUMPAD0) )
        {
            flag_OperationAllowed = false;
            break;
        }
}

I think you should also look into splitting the message handling cases in WindowProcedure up into separate functions - it's a pretty big function with a lot of things happening, and if you could reduce it to a switch that calls other functions, it'd be easier to see what it does at a glance.

You've also got a typo in the comments at the top:

   Ando so on upto a maximum of Slot 9. (Total 10 windows)

should be "And so on, up to a maximum (...)"

Lastly, as said by others already in the comments, this /////// style of yours is hard to maintain. It's also pretty annoying to read, in my opinion. Editing of text prefixed with a lot of slashes like that is harder than it would be without the slashes, and if you're in the business of habitually revising your comments due to changes made, the slashes lead to a lot of busy work.

If you need a visual indicator where your functions begin, there's a few options:

  • Document the functions, so the comment block at the start highlights a new function
  • Configure your IDE to give function declarations a different color
  • Make use of your IDE's ability to jump between function declarations
  • Split up your code into different files that provide a better overview. (Such as a header file and an implementation).

Now, that's regarding all the slashes for your functions.

In other places, they are down right dangerous:

////////////////////////////////////////////////
//// 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
//////////////////////////////////////////////////

I skipped those as comments.

That's what they are, aren't they? I just saw big blocks like this:

//////////////////////////////////////////////////////////////////
//// 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
//////////////////////////////////////////////////////////////////////

Which weren't that interesting, so I just skipped over the comments.

Except I just missed your includes and your defines. By putting all those slashes around them, you've actually made it HARDER to spot the defines. There's so many signs pointing "HERE!" that I can't see the actual thing.

If you really need the comment headers, try styles like this:

////////// Application Info //////////
// Name:        LifeSaver
// Version:     3.0
// Completed:   4 June 2015
// Purpose:     Hide specific windows from the screen whenever wanted


////////// Windows defines //////////
#define Windows7 0x601
#define _WIN32_WINNT Windows7
#define WIN32_LEAN_AND_MEAN


////////// Headers //////////
#include <windows.h>
#include <vector>


////////// Other defines //////////
//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


////////// Keystate defines //////////
// 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*/)
{

Make better use of whitespace, not slashes.

\$\endgroup\$
7
  • \$\begingroup\$ Woo, thanks. The flag flipping is done as I did just because it matched well with the if-else blocks... And about the second suggestion, OK I'll do that. \$\endgroup\$ Feb 12, 2016 at 9:19
  • \$\begingroup\$ @Pigmd But please suggest about the main questions I asked (if you can, obviously): 1. Is my way of killing captured windows/apps Robust enough ?? 2. How to force a window to repaint itself in a way better than mine ? By the way, how do you like it? \$\endgroup\$ Feb 12, 2016 at 9:22
  • \$\begingroup\$ @AneesAhmed777 I don't have enough knowledge about the windows api to be able to answer those questions. All I can do is offer you my view on your code as a programmer. It looks okay, you've followed some style conventions with your variable naming, I don't like the way you use your braces but that is a holy war I'm not willing to start, and you say it works, so it's good. \$\endgroup\$
    – Pimgd
    Feb 12, 2016 at 9:35
  • \$\begingroup\$ @AneesAhmed777 I also think you should look into splitting up WindowProcedure so it just calls separate functions, instead of doing everything inside the switch cases. \$\endgroup\$
    – Pimgd
    Feb 12, 2016 at 9:40
  • \$\begingroup\$ @Pigmd Hehehe, I'm still in school so I don't have any practical knowledge. "Make better use of whitespace, not slashes." OK, Roger that... \$\endgroup\$ Feb 12, 2016 at 9:41
1
\$\begingroup\$

You ask for a more robust way of killing all captured windows. If you want to make sure their processes are terminated, you can query them after sending the message and, if they didn't exit, terminate them forcibly. Please be aware that this is dangerous:

The TerminateProcess function is used to unconditionally cause a process to exit. The state of global data maintained by dynamic-link libraries (DLLs) may be compromised if TerminateProcess is used rather than ExitProcess.

Something like this: (untested, I'm currently on my linux machine. Treat this as you would treat pseudocode)

// ...

else if ( KILL_AND_DIE )
{
    unsigned int N = winObjects.size();
    std::vector<DWORD> v_processId(N);

    // Try to close all the windows...
    for (unsigned int i = 0; i < N; ++i)
    {
        // ... But first store their processes ids so we can query them later.
        DWORD processId = 0;
        GetWindowThreadProcessId(winObjects[i].hTargetWindow,&processId);
        v_processId[i] = processId;

        PostMessage(winObjects[i].hTargetWindow, WM_CLOSE, 0, 0);
        PostMessage(winObjects[i].hTargetWindow, WM_QUIT, 0, 0);
        PostMessage(winObjects[i].hTargetWindow, WM_CLOSE, 0, 0);
    }

    Sleep(1000) // Give some time (1 second, completely arbitrary) for the processes to respond.

    // Terminate the processes that didn't respond.
    for (unsigned int i = 0; i < N; ++i)
    {
        DWORD exitCode = 0;
        GetExitCodeProcess(v_processId[i], &exitCode);
        if (exitCode == STILL_ACTIVE) {
          TerminateProcess(v_processId[i],0);
        }
    }

    // Now close itself
    SendMessage(hSelf, WM_CLOSE, 0, 0);
}

// ...
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.