Skip to content

Commit 7b973f6

Browse files
committed
feat(finalshot): add exclusive window capture mode using PrintWindow API
- Introduce UsePrintWindow setting to toggle exclusive window capture - Implement PrintWindow API call for capturing window without overlaps - Add fallback to CopyFromScreen if PrintWindow fails or throws exception - Update logging to reflect capture mode and PrintWindow usage - Extend configuration and README with UsePrintWindow option details - Bump AssemblyVersion, skin definition version, and build script patch number
1 parent 4c65862 commit 7b973f6

File tree

6 files changed

+63
-8
lines changed

6 files changed

+63
-8
lines changed

Build.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#usage -----> powershell -ExecutionPolicy Bypass -Command "& {. .\Build.ps1; Dist -major 1 -minor 6 -patch 0}"
1+
#usage -----> powershell -ExecutionPolicy Bypass -Command "& {. .\Build.ps1; Dist -major 1 -minor 7 -patch 0}"
22
$msbuild = "C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\MSBuild.exe"
33

44
function Add-RMSkinFooter {

FinalShot/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Runtime.CompilerServices;
33

44
[assembly: AssemblyCopyright("© 2025 - nstechbytes 🇵🇰")]
5-
[assembly: AssemblyVersion("1.6.0.0")]
5+
[assembly: AssemblyVersion("1.7.0.0")]
66

77
// Do not change the entries below!
88
#if X64

FinalShot/FinalShot.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class Settings
6565
public int JpegQuality { get; private set; }
6666
public Rectangle PredefinedRegion { get; private set; }
6767
public bool ShowNotification { get; private set; }
68+
public bool UsePrintWindow { get; private set; }
6869

6970
public Settings(API api)
7071
{
@@ -74,6 +75,7 @@ public Settings(API api)
7475
ShowCursor = api.ReadInt("ShowCursor", 0) > 0;
7576
JpegQuality = api.ReadInt("JpgQuality", 70);
7677
ShowNotification = api.ReadInt("ShowNotification", 0) > 0;
78+
UsePrintWindow = api.ReadInt("UsePrintWindow", 0) > 0;
7779
int x = api.ReadInt("PredefX", 0);
7880
int y = api.ReadInt("PredefY", 0);
7981
int w = api.ReadInt("PredefWidth", 0);
@@ -117,6 +119,9 @@ private struct RECT
117119
[DllImport("user32.dll")]
118120
private static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
119121

122+
[DllImport("user32.dll")]
123+
private static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, uint nFlags);
124+
120125
[DllImport("user32.dll", SetLastError = true)]
121126
private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO pIconInfo);
122127

@@ -232,7 +237,7 @@ public static void TakeCustom(Settings settings, Action finishCallback)
232237

233238
public static void TakeWindowScreenshot(Settings settings, string windowTitle)
234239
{
235-
Logger.Log($"TakeWindowScreenshot() called. WindowTitle='{windowTitle}'");
240+
Logger.Log($"TakeWindowScreenshot() called. WindowTitle='{windowTitle}', UsePrintWindow={settings.UsePrintWindow}");
236241
if (string.IsNullOrEmpty(settings.SavePath))
237242
{
238243
Logger.Log("TakeWindowScreenshot: SavePath is empty, aborting.");
@@ -271,7 +276,41 @@ public static void TakeWindowScreenshot(Settings settings, string windowTitle)
271276
using (var bmp = new Bitmap(width, height))
272277
using (var g = Graphics.FromImage(bmp))
273278
{
274-
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
279+
if (settings.UsePrintWindow)
280+
{
281+
// Use PrintWindow API for exclusive window capture
282+
Logger.Log("TakeWindowScreenshot: Using PrintWindow API");
283+
IntPtr hdc = g.GetHdc();
284+
try
285+
{
286+
// PW_RENDERFULLCONTENT = 0x00000002
287+
bool result = PrintWindow(hWnd, hdc, 0x00000002);
288+
if (!result)
289+
{
290+
Logger.Log("TakeWindowScreenshot: PrintWindow failed, falling back to CopyFromScreen");
291+
g.ReleaseHdc(hdc);
292+
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
293+
}
294+
else
295+
{
296+
g.ReleaseHdc(hdc);
297+
Logger.Log("TakeWindowScreenshot: PrintWindow succeeded");
298+
}
299+
}
300+
catch (Exception ex)
301+
{
302+
Logger.Log($"TakeWindowScreenshot: PrintWindow exception: {ex.Message}");
303+
try { g.ReleaseHdc(hdc); } catch { }
304+
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
305+
}
306+
}
307+
else
308+
{
309+
// Use CopyFromScreen for screen-based capture (includes overlapping windows)
310+
Logger.Log("TakeWindowScreenshot: Using CopyFromScreen");
311+
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
312+
}
313+
275314
if (settings.ShowCursor)
276315
DrawCursor(g, bounds);
277316
SaveImageSafely(bmp, settings);

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ FinalShot supports full‑screen captures, predefined regions, custom selection,
3232
Draw a region on‑the‑fly with your mouse.
3333

3434
- **Window Capture** (`-ws|windowtitle`)
35-
Capture a specific window by its exact title.
35+
Capture a specific window by its exact title.
36+
Supports two modes: exclusive window capture (using `PrintWindow` API) or screen-based capture (includes overlapping windows).
3637

3738
- **Toast Notifications**
3839
Beautiful themed notifications that adapt to Windows Dark/Light mode with screenshot preview.
@@ -87,6 +88,8 @@ SavePath=C:\Users\You\Pictures\shot.png
8788
ShowCursor=1
8889
; 1 = show notification with preview, 0 = no notification
8990
ShowNotification=1
91+
; Window capture mode (1 = exclusive window, 0 = screen-based)
92+
UsePrintWindow=1
9093
; JPEG quality (only if SavePath ends in .jpg/.jpeg)
9194
JpgQuality=85
9295
; Predefined region (for -ps):
@@ -144,6 +147,7 @@ DebugLog=0
144147
| `ScreenshotFinishAction`| Rainmeter bang or command to run after saving. | (empty) |
145148
| `ShowCursor` | Include mouse cursor in capture? (1 = yes, 0 = no) | 0 |
146149
| `ShowNotification` | Show toast notification with preview after capture? (1 = yes, 0 = no) | 0 |
150+
| `UsePrintWindow` | Window capture mode: 1 = exclusive window (no overlaps), 0 = screen-based (includes overlaps) | 0 |
147151
| `JpgQuality` | JPEG compression quality (0–100). Only applies to `.jpg` or `.jpeg` files. | 70 |
148152
| `PredefX`, `PredefY` | Top‑left coordinates of the predefined capture region. | 0 |
149153
| `PredefWidth`, `PredefHeight` | Width & height of the predefined capture region. | 0 |
@@ -179,20 +183,32 @@ ShowCursor=0
179183
ShowNotification=1
180184
```
181185

182-
### Window Capture
186+
### Window Capture (Exclusive Mode)
183187
```ini
184188
[MeasureWindow]
185189
Measure=Plugin
186190
Plugin=FinalShot
187191
SavePath=#@#Screenshots\Window.png
188192
ShowCursor=0
189193
ShowNotification=1
194+
; Capture only the window (no overlapping windows)
195+
UsePrintWindow=1
190196

191197
[CaptureNotepad]
192198
Meter=String
193199
LeftMouseUpAction=[!CommandMeasure MeasureWindow "-ws|Untitled - Notepad"]
194200
```
195201

202+
### Window Capture (Screen-Based Mode)
203+
```ini
204+
[MeasureWindowScreen]
205+
Measure=Plugin
206+
Plugin=FinalShot
207+
SavePath=#@#Screenshots\WindowScreen.png
208+
; Capture whatever is visible on screen (includes overlapping windows)
209+
UsePrintWindow=0
210+
```
211+
196212
---
197213

198214
## Debug Logging

Resources/Skins/FinalShot/Main.ini

0 Bytes
Binary file not shown.

Resources/skin_definition.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"skinDir": ".\\Resources\\Skins",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"minimumVersion": "4.5",
55
"author": "nstechbytes",
66
"variableFiles": "",
@@ -17,5 +17,5 @@
1717
"load": "FinalShot\\Main.ini",
1818
"headerImage": ".\\Resources\\banner.bmp",
1919
"configPrefix": "FinalShot",
20-
"output": ".\\dist\\FinalShot_v1.6.0.rmskin"
20+
"output": ".\\dist\\FinalShot_v1.7.0.rmskin"
2121
}

0 commit comments

Comments
 (0)