From 99c0d4eb09a55552568dc644d4f095045d529d6e Mon Sep 17 00:00:00 2001 From: Ryan Ries Date: Mon, 8 Jun 2015 17:04:03 -0500 Subject: [PATCH] First Commit First Commit --- Main.cpp | 217 +++++++++++++++++++++++++++ UniversalPauseButton.rc | Bin 0 -> 5310 bytes UniversalPauseButton.sln | 28 ++++ UniversalPauseButton.vcxproj | 149 ++++++++++++++++++ UniversalPauseButton.vcxproj.filters | 37 +++++ pause.ico | Bin 0 -> 31702 bytes resource.h | Bin 0 -> 928 bytes 7 files changed, 431 insertions(+) create mode 100644 Main.cpp create mode 100644 UniversalPauseButton.rc create mode 100644 UniversalPauseButton.sln create mode 100644 UniversalPauseButton.vcxproj create mode 100644 UniversalPauseButton.vcxproj.filters create mode 100644 pause.ico create mode 100644 resource.h diff --git a/Main.cpp b/Main.cpp new file mode 100644 index 0000000..fcb225f --- /dev/null +++ b/Main.cpp @@ -0,0 +1,217 @@ +// Main.cpp +// UniversalPauseButton +// Ryan Ries, 2015 +// ryan@myotherpcisacloud.com +// +// Must compile in Unicode. + +#include +#include +#include +#include "resource.h" + +#define WM_TRAYICON (WM_USER + 1) + +typedef LONG(NTAPI* _NtSuspendProcess) (IN HANDLE ProcessHandle); +typedef LONG(NTAPI* _NtResumeProcess) (IN HANDLE ProcessHandle); + +_NtSuspendProcess NtSuspendProcess = (_NtSuspendProcess)GetProcAddress(GetModuleHandle(L"ntdll"), "NtSuspendProcess"); +_NtResumeProcess NtResumeProcess = (_NtResumeProcess)GetProcAddress(GetModuleHandle(L"ntdll"), "NtResumeProcess"); + +NOTIFYICONDATA G_TrayNotifyIconData; +HANDLE G_Mutex; + + +// The WindowProc (callback) for WinMain's WindowClass. +// Basically the system tray does nothing except lets the user know that it's running. +// If the user clicks the tray icon it will ask if they want to exit the app. +LRESULT CALLBACK WindowClassCallback(_In_ HWND Window, _In_ UINT Message, _In_ WPARAM WParam, _In_ LPARAM LParam) +{ + LRESULT Result = 0; + + switch (Message) + { + case WM_TRAYICON: + { + if (LParam == WM_LBUTTONDOWN || LParam == WM_RBUTTONDOWN) + { + if (MessageBox(Window, L"Quit UniversalPauseButton?", L"Are you sure?", MB_YESNO | MB_ICONQUESTION) == IDYES) + { + Shell_NotifyIcon(NIM_DELETE, &G_TrayNotifyIconData); + PostQuitMessage(0); + } + } + } + default: + { + Result = DefWindowProc(Window, Message, WParam, LParam); + break; + } + } + return(Result); +} + +// Entry point. +int CALLBACK WinMain(_In_ HINSTANCE Instance, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int) +{ + G_Mutex = CreateMutex(NULL, FALSE, L"UniversalPauseButton"); + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + MessageBox(NULL, L"An instance of the program is already running.", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + return(ERROR_ALREADY_EXISTS); + } + + WNDCLASS SysTrayWindowClass = { 0 }; + + SysTrayWindowClass.style = CS_HREDRAW | CS_VREDRAW; + SysTrayWindowClass.hInstance = Instance; + SysTrayWindowClass.lpszClassName = L"UniversalPauseButton_Systray_WindowClass"; + SysTrayWindowClass.hbrBackground = CreateSolidBrush(RGB(255, 0, 255)); + SysTrayWindowClass.lpfnWndProc = WindowClassCallback; + + if (RegisterClass(&SysTrayWindowClass) == 0) + { + MessageBox(NULL, L"Failed to register WindowClass!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + return(E_FAIL); + } + + HWND SystrayWindow = CreateWindowEx( + WS_EX_TOOLWINDOW, + SysTrayWindowClass.lpszClassName, + L"UniversalPauseButton_Systray_Window", + WS_ICONIC, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + 0, + 0, + Instance, + 0); + + if (SystrayWindow == 0) + { + MessageBox(NULL, L"Failed to create SystrayWindow!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + return(E_FAIL); + } + + G_TrayNotifyIconData.cbSize = sizeof(NOTIFYICONDATA); + G_TrayNotifyIconData.hWnd = SystrayWindow; + G_TrayNotifyIconData.uID = 1982; + G_TrayNotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + G_TrayNotifyIconData.uCallbackMessage = WM_TRAYICON; + + wcscpy_s(G_TrayNotifyIconData.szTip, L"Universal Pause Button v1.0"); + + G_TrayNotifyIconData.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, NULL); + + if (G_TrayNotifyIconData.hIcon == NULL) + { + MessageBox(NULL, L"Failed to load systray icon resource!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + return(E_FAIL); + } + + if (Shell_NotifyIcon(NIM_ADD, &G_TrayNotifyIconData) == FALSE) + { + MessageBox(NULL, L"Failed to register systray icon!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + return(E_FAIL); + } + + MSG SysTrayWindowMessage = { 0 }; + static int PauseKeyWasDown = 0; + DWORD PreviouslySuspendedProcessID = 0; + wchar_t PreviouslySuspendedProcessText[256] = { 0 }; + + while (SysTrayWindowMessage.message != WM_QUIT) + { + while (PeekMessage(&SysTrayWindowMessage, SystrayWindow, 0, 0, PM_REMOVE)) + { + DispatchMessage(&SysTrayWindowMessage); + } + + int PauseKeyIsDown = GetAsyncKeyState(VK_PAUSE); + + if (PauseKeyIsDown && !PauseKeyWasDown) + { + HWND ForegroundWindow = GetForegroundWindow(); + if (ForegroundWindow) + { + DWORD ProcessID = 0; + GetWindowThreadProcessId(ForegroundWindow, &ProcessID); + if (ProcessID != 0) + { + HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID); + if (ProcessHandle != 0) + { + if (PreviouslySuspendedProcessID == 0) + { + NtSuspendProcess(ProcessHandle); + PreviouslySuspendedProcessID = ProcessID; + GetWindowText(ForegroundWindow, PreviouslySuspendedProcessText, sizeof(PreviouslySuspendedProcessText) / sizeof(wchar_t)); + } + else if (PreviouslySuspendedProcessID == ProcessID) + { + NtResumeProcess(ProcessHandle); + PreviouslySuspendedProcessID = 0; + memset(PreviouslySuspendedProcessText, 0, sizeof(PreviouslySuspendedProcessText)); + } + else + { + // The user pressed the pause button while focused on another process than what was + // originally paused and the first process is still paused. + DWORD AllProcesses[2048] = { 0 }; + DWORD BytesReturned = 0; + BOOL PreviouslySuspendedProcessIsStillRunning = FALSE; + + if (EnumProcesses(AllProcesses, sizeof(AllProcesses), &BytesReturned) != 0) + { + for (DWORD Counter = 0; Counter < (BytesReturned / sizeof(DWORD)); Counter++) + { + if ((AllProcesses[Counter] != 0) && AllProcesses[Counter] == PreviouslySuspendedProcessID) + { + PreviouslySuspendedProcessIsStillRunning = TRUE; + } + } + if (PreviouslySuspendedProcessIsStillRunning) + { + wchar_t MessageBoxBuffer[1024] = { 0 }; + _snwprintf_s(MessageBoxBuffer, sizeof(MessageBoxBuffer), L"You must first unpause %s (PID %d) before pausing another program.", PreviouslySuspendedProcessText, PreviouslySuspendedProcessID); + MessageBox(ForegroundWindow, MessageBoxBuffer, L"Universal Pause Button", MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL); + } + else + { + // The paused process is no more, so reset. + PreviouslySuspendedProcessID = 0; + memset(PreviouslySuspendedProcessText, 0, sizeof(PreviouslySuspendedProcessText)); + } + } + else + { + MessageBox(NULL, L"EnumProcesses failed!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + } + } + CloseHandle(ProcessHandle); + } + else + { + MessageBox(NULL, L"OpenProcess failed!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + } + } + else + { + MessageBox(NULL, L"Unable to get process ID of foreground window!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + } + } + else + { + MessageBox(NULL, L"Unable to detect foreground window!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR); + } + } + + PauseKeyWasDown = PauseKeyIsDown; + + Sleep(10); + } + + return(S_OK); +} \ No newline at end of file diff --git a/UniversalPauseButton.rc b/UniversalPauseButton.rc new file mode 100644 index 0000000000000000000000000000000000000000..1509512ad03cfdbba653da9a90eb14b3133d4055 GIT binary patch literal 5310 zcmdUzU2oz>6o$`rrT&L0x!F}$3F&SxdbtUNq7p#MhgvBTN<&DI5{NMDZlwP9cAs~K ziEV7s4QVAR%O3m8nKS3}^|Al{>&TAG*||;a$`&@agmYp`XhUd;t?bfnc{;W4Ih@_t zwaqve(3gy3XbVQITXA>Az2iyJrgjg{p?z!Lu*;Dj51`JNy@h-1c`JqWhxUh6JhxUJ zc)p2WC)w|**?y!G}cjSjYR{zSiiPfjrDSyE!k$;ZCC-2{|F7N;S^H|g+0wt{3TS9#0{jXIC-q+dBZJ?Nckv9oUF3JjzyS!<|Ed*H`kBX$2HKs2Aw6o}A^o$x zC4wz)kFqjF4WU~d#2HW}c|B7}LBjFl`fHFl!B0=~S2O&2PifcuiVpe(+3(O%$Gi2K z)LVV}nJyaUbx7v}Ugo*t&gHc)bnW0*d8UE}OX980?1l(UvFw^S&CqqeQH|x&VpZg& z%95)6`97rN(dH?s%w?vkf#=z4cl$eIT~W=nS8#J{*}9P^%tyXPx5?s|+x)yMNWRw?@GD;A8>cGJg2ds1t%wj3hk zGv{;mZoz-!cU7G4iJEfrhMs5n7~|rM`k_Y}S)(vQ#5yR+6)btIp~*yt&n zpZ}%ry67+`lUHQ&@++d|z?GM0VSd-$b205WVzw74d5jsN{o>KnyI@wnp87}=swBiu zy>zyU^#%!uCY(9;IZ|DtdC)i2HoX(t>&(7!_v=1t7e@#73kQpDe(9agI^V_Vd(?RZ z{Y2d+_)Hu%t{?HBzVr39dhh+IzXSSXFu||FLGcRn9CZ0ye-Old3BOk8uS6OJ=by8B zg)iMZcvN%3`JlHt7fF{??V&Z2dds1|6A$azJI3&BERCDKi#g@J*!CTD%)zp;3D$_O zY_5z}!Jb%Whif|mCT{O8|DEr>{mQkhHnB3|7<#g&zUGVFbynVf-N$N*<4gbR=xgWc d|2ol+`{jqMHqvl=zR##rRcrAtPBHG1^aZWHhhP8z literal 0 HcmV?d00001 diff --git a/UniversalPauseButton.sln b/UniversalPauseButton.sln new file mode 100644 index 0000000..e45b56e --- /dev/null +++ b/UniversalPauseButton.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UniversalPauseButton", "UniversalPauseButton.vcxproj", "{229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Debug|Win32.ActiveCfg = Debug|Win32 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Debug|Win32.Build.0 = Debug|Win32 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Debug|x64.ActiveCfg = Debug|x64 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Debug|x64.Build.0 = Debug|x64 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Release|Win32.ActiveCfg = Release|Win32 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Release|Win32.Build.0 = Release|Win32 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Release|x64.ActiveCfg = Release|x64 + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/UniversalPauseButton.vcxproj b/UniversalPauseButton.vcxproj new file mode 100644 index 0000000..32a3a4c --- /dev/null +++ b/UniversalPauseButton.vcxproj @@ -0,0 +1,149 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {229438CB-2B5E-4C34-B1CE-F9AC14B0C47C} + UniversalPauseButton + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + + Level4 + Disabled + true + MultiThreadedDebug + + + true + + + + + Level4 + Disabled + true + MultiThreadedDebug + + + true + + + + + Level4 + MaxSpeed + true + true + true + MultiThreaded + + + true + true + true + + + + + Level4 + MaxSpeed + true + true + true + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UniversalPauseButton.vcxproj.filters b/UniversalPauseButton.vcxproj.filters new file mode 100644 index 0000000..dd4f00f --- /dev/null +++ b/UniversalPauseButton.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/pause.ico b/pause.ico new file mode 100644 index 0000000000000000000000000000000000000000..6d793b1c135e80df5bd75eb007d9cef17fd9083e GIT binary patch literal 31702 zcmeHQv5q4}5N)$?wn%mp2$0|$Zpa6K&hP~U1O(``NC+JX0YXS1B=3n3iJu_gz5oe{ zU{?f$F z<99>&M0!hq6pmjF;d_aV$IJ02M1XHEDX9xF!E<(QSzlh2y)Jv>&7rpqxarzf8^;d^+j7)_UBR!&boYr^;NSTUL| z$E=*5eAa~T;jv;gU5;5fJ^8E&-@{|YXu2G;a(ePv6TXMXiqUjAX65wcvnG5Gj}@co za?Hx<$!AUY9v&-3)8&|z)05Ad@I5?MjHb&mE2k%)HQ{@BtQbv~V^&U2K5N4F@K`aL zF2}5#o_yAX@8PjxG+mBaIX(HT3E#tG#b~-5vvPX!Srfj8$BNN(IcDYb2l1<>B(nJ_#PfBM$_e(mD7{Yn(#e5R*a_0F)ODhpEcooc&r#rmt$5=Pd;nH_wZOT znl8tzoSuBvgzw?8Vl-WjSvfuVtO?)4W5sB?9J6wI@>vtUhsTQ1bU9|_^yIT9d=HNm zqv>+Y%IV2xP52%jD@N1hn3dC$&zkT(JXVaR%P}jbC!aOpdw8rEO_yUF)`aijv0^k`j#)W9`K$@w!(+v0x*W4|dh%HlzK6$((R4Xx<@DsUCVUT% z6{G2L%*yG>XHEDX9xF!E<(QSzlh2y)Jv>&7rpqxarzf8^;d^+j7)_UBR!&boYr^;N zSTUL|$E=*5eAa~T;jv;gU5;5fJ^8E&-@{|YXu2FL^*y{&y1f4vdp;K6*IFrEo=d6k z;g-_n{eMpa*7ubqAHO5gS}9$g%f7#d4}Ptc($(g&uis&SH`Pk%YI7>}JDgIwTm!5} z%q91CK)3bxU$GuoliuG00k>_Ln=OF|+l}Vc9+Ezdic1NUk{(si*EBZJ*G4fHE#qpE>CrCudKXmqdbAt9O6h>N2+|qp z(ayaG41;=Y)}z}qs(wcWn(Eg2ZCha-)vN2juAfx}(kmoATHk;5{g+-L>Cw7=>iS8q zko0I>KXv`2S4eua`3UStX~sIsyBh5#SSP6T>rr&HjH`t7=rV63=L)XzUmI;4BA|ry zC?($_cjNJ4e6ARuGuG$CH|n|fKAsbgWpPi$1_3>3uNbzaK0h-hgj;F`9CuLl^C}iEVu?@KZ~Wb;~3`9u0sx1sG9b+yhi>$+w&C; zn;dMUv3`dF>mjxrY~+|qr_X-3b9RB<1xL=@tNyYAIW^3|*811_qXIc=CGvU?a^d()m-Y?;LQC6k859a$x(h zSRZ}dBjH}F-Ey#z2lp}HN!0=ONUujgYny{@(KKDu^F$2mYPvGqW4t8>om9)grg--< zhUY|=hx7aM;p}jBa5kI`&JOlz^Ml#&ayUPX&!_Nxwz1emZ2f(wMtUypniR1Z{a ztP->_#M%viBWvg3Jrmp4(rJ&OuiaC|{N7vq@ap{A5FS6a+4-+BA;IE)=;}PPI>Ef4 b%JclIzj;h+O=?a4uWLX(DcqG{?s@hnL~3=FVO@t~K6@ zDmffWZe#Myz0g#T=ozuq242IvAilx#^5Xwy8ecCuo=KCv>?jdd$K8JLj5M zpJ?lQpj%L4zC(5K4A6JYEB&sWlGD=-98wuhOiqFpprS5T;ByAV_acw^9f2hUwZDY2%X8OPFFJWYY AZvX%Q literal 0 HcmV?d00001