├── .gitignore ├── WinShiftS ├── sc.ico ├── WinShiftS.csproj └── WinShiftS.cs ├── README.md └── WinShiftS.sln /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | */obj/ 3 | */bin/ -------------------------------------------------------------------------------- /WinShiftS/sc.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsmattkc/WinShiftS/HEAD/WinShiftS/sc.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Win+Shift+S 2 | 3 | Download: [Latest Release](https://github.com/itsmattkc/WinShiftS/releases/download/continuous/WinShiftS.exe) 4 | 5 | Lightweight screenshotting tool that imitates the functionality of Win+Shift+S in Windows 10 1703+ but in much earlier versions of Windows. 6 | 7 | After opening this tool, pressing Win+Shift+S will allow you to make a rectangular selection of the screen and the resulting snippet of screenshot will be copied to the clipboard ready for pasting anywhere. 8 | 9 | This tool does not automatically install itself and will need to be manually made a "startup item" by a method of your choice. 10 | 11 | This tool is compatible with the following versions of Windows: 12 | * Windows 98/98SE 13 | * Windows 2000 14 | * Windows Me 15 | * Windows XP 16 | * Windows Server 2003 (Web/Standard) 17 | * Windows Vista 18 | * Windows 7 19 | * Windows 8/8.1 20 | * Windows 10 prior to 1703 21 | -------------------------------------------------------------------------------- /WinShiftS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.438 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinShiftS", "WinShiftS\WinShiftS.csproj", "{33C76194-8094-4682-A053-D0632C960DC1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {33C76194-8094-4682-A053-D0632C960DC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {33C76194-8094-4682-A053-D0632C960DC1}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {33C76194-8094-4682-A053-D0632C960DC1}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {33C76194-8094-4682-A053-D0632C960DC1}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {3BDD9BF2-C2CA-4E81-A32F-7F9EDAE61E65} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /WinShiftS/WinShiftS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {33C76194-8094-4682-A053-D0632C960DC1} 8 | WinExe 9 | WinShiftS 10 | WinShiftS 11 | v2.0 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | sc.ico 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Form 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /WinShiftS/WinShiftS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Windows.Forms; 5 | using System.IO; 6 | using System.Drawing; 7 | using System.Drawing.Imaging; 8 | using System.Threading; 9 | using System.Reflection; 10 | 11 | namespace WinShiftS 12 | { 13 | public class WinShiftS : Form 14 | { 15 | [System.Runtime.InteropServices.DllImport("user32.dll")] 16 | private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); 17 | 18 | [System.Runtime.InteropServices.DllImport("user32.dll")] 19 | private static extern bool UnregisterHotKey(IntPtr hWnd, int id); 20 | 21 | private bool dragging; 22 | private Point drag_start; 23 | private Point drag_end; 24 | 25 | private Rectangle last_drawn; 26 | 27 | private Bitmap full_shot; 28 | 29 | private NotifyIcon systray_icon; 30 | 31 | public WinShiftS() 32 | { 33 | // Registers Win+Shift+S 34 | RegisterHotKey(this.Handle, 0, 0xC, (uint) Keys.S.GetHashCode()); 35 | 36 | this.TopMost = true; 37 | this.FormBorderStyle = FormBorderStyle.None; 38 | this.DoubleBuffered = true; 39 | 40 | this.Paint += new PaintEventHandler(this.ScreenshotPaint); 41 | this.MouseDown += new MouseEventHandler(this.UserMouseDown); 42 | this.MouseMove += new MouseEventHandler(this.UserMouseMove); 43 | this.MouseUp += new MouseEventHandler(this.UserMouseRelease); 44 | this.VisibleChanged += new EventHandler(this.CleanUpImage); 45 | this.FormClosing += new FormClosingEventHandler(this.OverrideClose); 46 | 47 | this.Cursor = Cursors.Cross; 48 | 49 | systray_icon = new NotifyIcon() 50 | { 51 | Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location), 52 | ContextMenu = new ContextMenu(new MenuItem[]{ 53 | new MenuItem("Exit", Exit) 54 | }), 55 | Visible = true 56 | }; 57 | ShowNotification("Win+Shift+S enabled"); 58 | } 59 | 60 | ~WinShiftS() 61 | { 62 | UnregisterHotKey(this.Handle, 0); 63 | } 64 | 65 | private void Exit(object sender, EventArgs e) 66 | { 67 | // Ensure system tray icon is hidden before we close 68 | systray_icon.Visible = false; 69 | 70 | Application.Exit(); 71 | } 72 | 73 | private void OverrideClose(object sender, FormClosingEventArgs e) 74 | { 75 | if (e.CloseReason == CloseReason.UserClosing) 76 | { 77 | // Closing the form will delete it and unregister the hotkey, which in the case of UserClosing (usually 78 | // Alt+F4) is probably not the user's intention. We allow all other closes however. 79 | e.Cancel = true; 80 | this.Hide(); 81 | } 82 | } 83 | 84 | private void UpdateChanged() 85 | { 86 | Invalidate(last_drawn); 87 | last_drawn = DragCoordsToRectangle(); 88 | 89 | // We draw a 1px border around the rectangle that needs to be invalidated graphically too 90 | last_drawn.X--; 91 | last_drawn.Y--; 92 | last_drawn.Width += 2; 93 | last_drawn.Height += 2; 94 | 95 | Invalidate(last_drawn); 96 | } 97 | 98 | private Rectangle DragCoordsToRectangle() 99 | { 100 | int drag_left = Math.Min(drag_start.X, drag_end.X); 101 | int drag_top = Math.Min(drag_start.Y, drag_end.Y); 102 | int drag_right = Math.Max(drag_start.X, drag_end.X); 103 | int drag_bottom = Math.Max(drag_start.Y, drag_end.Y); 104 | int drag_width = drag_right - drag_left; 105 | int drag_height = drag_bottom - drag_top; 106 | 107 | return new Rectangle(drag_left, drag_top, drag_width, drag_height); 108 | } 109 | 110 | private void ScreenshotPaint(object sender, System.Windows.Forms.PaintEventArgs e) 111 | { 112 | Graphics g = e.Graphics; 113 | 114 | g.DrawImage(full_shot, 0, 0); 115 | 116 | SolidBrush overlay = new SolidBrush(Color.FromArgb(128, 255, 255, 255)); 117 | 118 | if (dragging) 119 | { 120 | Rectangle r = DragCoordsToRectangle(); 121 | 122 | g.FillRectangle(overlay, 0, 0, Width - (Width - r.Left), Height); 123 | g.FillRectangle(overlay, r.Right, 0, Width - r.Right, Height); 124 | g.FillRectangle(overlay, r.Left, 0, r.Width, Height - (Height - r.Top)); 125 | g.FillRectangle(overlay, r.Left, r.Bottom, r.Width, Height - r.Bottom); 126 | 127 | g.DrawRectangle(new Pen(SystemColors.Highlight), r.Left, r.Top, r.Width, r.Height); 128 | } 129 | else 130 | { 131 | g.FillRectangle(overlay, 0, 0, Width, Height); 132 | } 133 | } 134 | 135 | protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 136 | { 137 | if (keyData == (Keys.Escape)) 138 | { 139 | this.Hide(); 140 | return true; 141 | } 142 | return base.ProcessCmdKey(ref msg, keyData); 143 | } 144 | 145 | private void UserMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) 146 | { 147 | dragging = true; 148 | drag_start = e.Location; 149 | drag_end = e.Location; 150 | } 151 | 152 | private void UserMouseMove(object sender, System.Windows.Forms.MouseEventArgs e) 153 | { 154 | if (dragging) 155 | { 156 | drag_end = e.Location; 157 | UpdateChanged(); 158 | } 159 | } 160 | 161 | private void ShowNotification(string text) 162 | { 163 | systray_icon.ShowBalloonTip(1000, "Win+Shift+S", text, ToolTipIcon.Info); 164 | } 165 | 166 | private void UserMouseRelease(object sender, System.Windows.Forms.MouseEventArgs e) 167 | { 168 | if (dragging) 169 | { 170 | if (drag_start.X != drag_end.X && drag_start.Y != drag_end.Y) 171 | { 172 | Bitmap cropped = full_shot.Clone(DragCoordsToRectangle(), full_shot.PixelFormat); 173 | 174 | Clipboard.SetImage(cropped); 175 | 176 | this.Hide(); 177 | 178 | ShowNotification("Screenshot copied to clipboard"); 179 | } 180 | 181 | dragging = false; 182 | } 183 | } 184 | 185 | private void CleanUpImage(object sender, EventArgs e) 186 | { 187 | if (!this.Visible) 188 | { 189 | // Attempt to clear the bitmap's memory if the form was hidden 190 | full_shot = null; 191 | } 192 | } 193 | 194 | private void TakeScreenshot() 195 | { 196 | int screenLeft = SystemInformation.VirtualScreen.Left; 197 | int screenTop = SystemInformation.VirtualScreen.Top; 198 | int screenWidth = SystemInformation.VirtualScreen.Width; 199 | int screenHeight = SystemInformation.VirtualScreen.Height; 200 | 201 | full_shot = new Bitmap(screenWidth, screenHeight, PixelFormat.Format24bppRgb); 202 | 203 | using (Graphics graphics = Graphics.FromImage(full_shot)) 204 | { 205 | graphics.CopyFromScreen(screenLeft, screenTop, 0, 0, full_shot.Size); 206 | } 207 | 208 | this.Show(); 209 | this.Location = new Point(screenLeft, screenTop); 210 | this.Size = full_shot.Size; 211 | 212 | // Set some default valuse 213 | dragging = false; 214 | drag_start = new Point(0, 0); 215 | drag_end = new Point(0, 0); 216 | } 217 | 218 | protected override void WndProc(ref Message m) 219 | { 220 | base.WndProc(ref m); 221 | 222 | if (m.Msg == 0x0312) 223 | { 224 | // Keeping these here just in case we end up using more hotkeys later 225 | 226 | //Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF); // The key of the hotkey that was pressed. 227 | //KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF); // The modifier of the hotkey that was pressed. 228 | //int id = m.WParam.ToInt32(); // The id of the hotkey that was pressed. 229 | 230 | TakeScreenshot(); 231 | } 232 | } 233 | 234 | [STAThread] 235 | static void Main() 236 | { 237 | // Ensure only one instance of this app is open at once 238 | bool createdNew; 239 | using (Mutex mutex = new Mutex(true, "Win+Shift+S", out createdNew)) 240 | { 241 | if (createdNew) 242 | { 243 | WinShiftS entity = new WinShiftS(); 244 | 245 | Application.Run(); 246 | } 247 | } 248 | } 249 | } 250 | } 251 | --------------------------------------------------------------------------------