1+ // Main.cpp
2+ // UniversalPauseButton
3+ // Ryan Ries, 2015
4+ // ryan@myotherpcisacloud.com
5+ //
6+ // Must compile in Unicode.
7+
8+ #include < Windows.h>
9+ #include < Psapi.h>
10+ #include < stdio.h>
11+ #include " resource.h"
12+
13+ #define WM_TRAYICON (WM_USER + 1 )
14+
15+ typedef LONG (NTAPI* _NtSuspendProcess) (IN HANDLE ProcessHandle);
16+ typedef LONG (NTAPI* _NtResumeProcess) (IN HANDLE ProcessHandle);
17+
18+ _NtSuspendProcess NtSuspendProcess = (_NtSuspendProcess)GetProcAddress(GetModuleHandle(L" ntdll" ), " NtSuspendProcess" );
19+ _NtResumeProcess NtResumeProcess = (_NtResumeProcess)GetProcAddress(GetModuleHandle(L" ntdll" ), " NtResumeProcess" );
20+
21+ NOTIFYICONDATA G_TrayNotifyIconData;
22+ HANDLE G_Mutex;
23+
24+
25+ // The WindowProc (callback) for WinMain's WindowClass.
26+ // Basically the system tray does nothing except lets the user know that it's running.
27+ // If the user clicks the tray icon it will ask if they want to exit the app.
28+ LRESULT CALLBACK WindowClassCallback (_In_ HWND Window, _In_ UINT Message, _In_ WPARAM WParam, _In_ LPARAM LParam)
29+ {
30+ LRESULT Result = 0 ;
31+
32+ switch (Message)
33+ {
34+ case WM_TRAYICON:
35+ {
36+ if (LParam == WM_LBUTTONDOWN || LParam == WM_RBUTTONDOWN)
37+ {
38+ if (MessageBox (Window, L" Quit UniversalPauseButton?" , L" Are you sure?" , MB_YESNO | MB_ICONQUESTION) == IDYES)
39+ {
40+ Shell_NotifyIcon (NIM_DELETE, &G_TrayNotifyIconData);
41+ PostQuitMessage (0 );
42+ }
43+ }
44+ }
45+ default :
46+ {
47+ Result = DefWindowProc (Window, Message, WParam, LParam);
48+ break ;
49+ }
50+ }
51+ return (Result);
52+ }
53+
54+ // Entry point.
55+ int CALLBACK WinMain (_In_ HINSTANCE Instance, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int )
56+ {
57+ G_Mutex = CreateMutex (NULL , FALSE , L" UniversalPauseButton" );
58+ if (GetLastError () == ERROR_ALREADY_EXISTS)
59+ {
60+ MessageBox (NULL , L" An instance of the program is already running." , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
61+ return (ERROR_ALREADY_EXISTS);
62+ }
63+
64+ WNDCLASS SysTrayWindowClass = { 0 };
65+
66+ SysTrayWindowClass.style = CS_HREDRAW | CS_VREDRAW;
67+ SysTrayWindowClass.hInstance = Instance;
68+ SysTrayWindowClass.lpszClassName = L" UniversalPauseButton_Systray_WindowClass" ;
69+ SysTrayWindowClass.hbrBackground = CreateSolidBrush (RGB (255 , 0 , 255 ));
70+ SysTrayWindowClass.lpfnWndProc = WindowClassCallback;
71+
72+ if (RegisterClass (&SysTrayWindowClass) == 0 )
73+ {
74+ MessageBox (NULL , L" Failed to register WindowClass!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
75+ return (E_FAIL);
76+ }
77+
78+ HWND SystrayWindow = CreateWindowEx (
79+ WS_EX_TOOLWINDOW,
80+ SysTrayWindowClass.lpszClassName ,
81+ L" UniversalPauseButton_Systray_Window" ,
82+ WS_ICONIC,
83+ CW_USEDEFAULT,
84+ CW_USEDEFAULT,
85+ CW_USEDEFAULT,
86+ CW_USEDEFAULT,
87+ 0 ,
88+ 0 ,
89+ Instance,
90+ 0 );
91+
92+ if (SystrayWindow == 0 )
93+ {
94+ MessageBox (NULL , L" Failed to create SystrayWindow!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
95+ return (E_FAIL);
96+ }
97+
98+ G_TrayNotifyIconData.cbSize = sizeof (NOTIFYICONDATA);
99+ G_TrayNotifyIconData.hWnd = SystrayWindow;
100+ G_TrayNotifyIconData.uID = 1982 ;
101+ G_TrayNotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
102+ G_TrayNotifyIconData.uCallbackMessage = WM_TRAYICON;
103+
104+ wcscpy_s (G_TrayNotifyIconData.szTip , L" Universal Pause Button v1.0" );
105+
106+ G_TrayNotifyIconData.hIcon = (HICON)LoadImage (GetModuleHandle (NULL ), MAKEINTRESOURCE (IDI_ICON1), IMAGE_ICON, 0 , 0 , NULL );
107+
108+ if (G_TrayNotifyIconData.hIcon == NULL )
109+ {
110+ MessageBox (NULL , L" Failed to load systray icon resource!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
111+ return (E_FAIL);
112+ }
113+
114+ if (Shell_NotifyIcon (NIM_ADD, &G_TrayNotifyIconData) == FALSE )
115+ {
116+ MessageBox (NULL , L" Failed to register systray icon!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
117+ return (E_FAIL);
118+ }
119+
120+ MSG SysTrayWindowMessage = { 0 };
121+ static int PauseKeyWasDown = 0 ;
122+ DWORD PreviouslySuspendedProcessID = 0 ;
123+ wchar_t PreviouslySuspendedProcessText[256 ] = { 0 };
124+
125+ while (SysTrayWindowMessage.message != WM_QUIT)
126+ {
127+ while (PeekMessage (&SysTrayWindowMessage, SystrayWindow, 0 , 0 , PM_REMOVE))
128+ {
129+ DispatchMessage (&SysTrayWindowMessage);
130+ }
131+
132+ int PauseKeyIsDown = GetAsyncKeyState (VK_PAUSE);
133+
134+ if (PauseKeyIsDown && !PauseKeyWasDown)
135+ {
136+ HWND ForegroundWindow = GetForegroundWindow ();
137+ if (ForegroundWindow)
138+ {
139+ DWORD ProcessID = 0 ;
140+ GetWindowThreadProcessId (ForegroundWindow, &ProcessID);
141+ if (ProcessID != 0 )
142+ {
143+ HANDLE ProcessHandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE , ProcessID);
144+ if (ProcessHandle != 0 )
145+ {
146+ if (PreviouslySuspendedProcessID == 0 )
147+ {
148+ NtSuspendProcess (ProcessHandle);
149+ PreviouslySuspendedProcessID = ProcessID;
150+ GetWindowText (ForegroundWindow, PreviouslySuspendedProcessText, sizeof (PreviouslySuspendedProcessText) / sizeof (wchar_t ));
151+ }
152+ else if (PreviouslySuspendedProcessID == ProcessID)
153+ {
154+ NtResumeProcess (ProcessHandle);
155+ PreviouslySuspendedProcessID = 0 ;
156+ memset (PreviouslySuspendedProcessText, 0 , sizeof (PreviouslySuspendedProcessText));
157+ }
158+ else
159+ {
160+ // The user pressed the pause button while focused on another process than what was
161+ // originally paused and the first process is still paused.
162+ DWORD AllProcesses[2048 ] = { 0 };
163+ DWORD BytesReturned = 0 ;
164+ BOOL PreviouslySuspendedProcessIsStillRunning = FALSE ;
165+
166+ if (EnumProcesses (AllProcesses, sizeof (AllProcesses), &BytesReturned) != 0 )
167+ {
168+ for (DWORD Counter = 0 ; Counter < (BytesReturned / sizeof (DWORD)); Counter++)
169+ {
170+ if ((AllProcesses[Counter] != 0 ) && AllProcesses[Counter] == PreviouslySuspendedProcessID)
171+ {
172+ PreviouslySuspendedProcessIsStillRunning = TRUE ;
173+ }
174+ }
175+ if (PreviouslySuspendedProcessIsStillRunning)
176+ {
177+ wchar_t MessageBoxBuffer[1024 ] = { 0 };
178+ _snwprintf_s (MessageBoxBuffer, sizeof (MessageBoxBuffer), L" You must first unpause %s (PID %d) before pausing another program." , PreviouslySuspendedProcessText, PreviouslySuspendedProcessID);
179+ MessageBox (ForegroundWindow, MessageBoxBuffer, L" Universal Pause Button" , MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL);
180+ }
181+ else
182+ {
183+ // The paused process is no more, so reset.
184+ PreviouslySuspendedProcessID = 0 ;
185+ memset (PreviouslySuspendedProcessText, 0 , sizeof (PreviouslySuspendedProcessText));
186+ }
187+ }
188+ else
189+ {
190+ MessageBox (NULL , L" EnumProcesses failed!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
191+ }
192+ }
193+ CloseHandle (ProcessHandle);
194+ }
195+ else
196+ {
197+ MessageBox (NULL , L" OpenProcess failed!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
198+ }
199+ }
200+ else
201+ {
202+ MessageBox (NULL , L" Unable to get process ID of foreground window!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
203+ }
204+ }
205+ else
206+ {
207+ MessageBox (NULL , L" Unable to detect foreground window!" , L" UniversalPauseButton Error" , MB_OK | MB_ICONERROR);
208+ }
209+ }
210+
211+ PauseKeyWasDown = PauseKeyIsDown;
212+
213+ Sleep (10 );
214+ }
215+
216+ return (S_OK);
217+ }
0 commit comments