• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dapplo / Dapplo.Windows / 22231723592

20 Feb 2026 04:16PM UTC coverage: 33.416% (-0.07%) from 33.49%
22231723592

push

github

Lakritzator
Small fixes for tests and namespaces

611 of 1948 branches covered (31.37%)

Branch coverage included in aggregate %.

2 of 8 new or added lines in 2 files covered. (25.0%)

231 existing lines in 16 files now uncovered.

1668 of 4872 relevant lines covered (34.24%)

30.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.02
/src/Dapplo.Windows/Desktop/InteropWindowExtensions.cs
1
// Copyright (c) Dapplo and contributors. All rights reserved.
2
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3

4
using System;
5
using System.Collections.Generic;
6
using System.ComponentModel;
7
using System.Drawing;
8
using System.Threading.Tasks;
9
using Dapplo.Windows.App;
10
using Dapplo.Windows.Common;
11
using Dapplo.Windows.Common.Extensions;
12
using Dapplo.Windows.Common.Structs;
13
using Dapplo.Windows.Enums;
14
using Dapplo.Windows.DesktopWindowsManager;
15
using Dapplo.Windows.Gdi32;
16
using Dapplo.Windows.User32;
17
using Dapplo.Windows.User32.Enums;
18
using Dapplo.Windows.User32.Structs;
19
using System.Drawing.Imaging;
20
using System.Linq;
21
using Dapplo.Log;
22
#if !NETSTANDARD2_0
23
using Dapplo.Windows.Extensions;
24
#endif
25
using Dapplo.Windows.Kernel32;
26

27
namespace Dapplo.Windows.Desktop;
28

29
/// <summary>
30
///     Extensions for the interopWindow, all get or set commands update the value in the InteropWindow that is used.
31
/// </summary>
32
public static class InteropWindowExtensions
33
{
34
    private static readonly LogSource Log = new LogSource();
×
35

36
    /// <summary>
37
    /// Tests if the interopWindow still exists
38
    /// </summary>
39
    /// <param name="interopWindow">IInteropWindow</param>
40
    /// <returns>True if it's still there.
41
    /// Because window handles are recycled the handle could point to a different window!
42
    /// </returns>
43
    public static bool Exists(this IInteropWindow interopWindow)
44
    {
45
        return User32Api.IsWindow(interopWindow.Handle);
×
46
    }
47

48
    /// <summary>
49
    ///     Fill ALL the information of the InteropWindow
50
    /// </summary>
51
    /// <param name="interopWindow">InteropWindow</param>
52
    /// <param name="retrieveSettings">InteropWindowRetrieveSettings to specify which information is retrieved and what not</param>
53
    /// <returns>IInteropWindow for fluent calls</returns>
54
    public static IInteropWindow Fill(this IInteropWindow interopWindow, InteropWindowRetrieveSettings retrieveSettings = InteropWindowRetrieveSettings.CacheAllAutoCorrect)
55
    {
56
        if ((retrieveSettings & InteropWindowRetrieveSettings.Children) != 0 && (retrieveSettings & InteropWindowRetrieveSettings.ZOrderedChildren) != 0)
2!
57
        {
58
            throw new ArgumentException("Can't have both Children & ZOrderedChildren", nameof(retrieveSettings));
×
59
        }
60
        var forceUpdate = (retrieveSettings | InteropWindowRetrieveSettings.ForceUpdate) != 0;
2✔
61
        var autoCorrect = (retrieveSettings | InteropWindowRetrieveSettings.AutoCorrectValues) != 0;
2✔
62

63
        if ((retrieveSettings & InteropWindowRetrieveSettings.Info) != 0)
2✔
64
        {
65
            interopWindow.GetInfo(forceUpdate, autoCorrect);
2✔
66
        }
67
        if ((retrieveSettings & InteropWindowRetrieveSettings.Caption) != 0)
2✔
68
        {
69
            interopWindow.GetCaption(forceUpdate);
2✔
70
        }
71
        if ((retrieveSettings & InteropWindowRetrieveSettings.Classname) != 0)
2✔
72
        {
73
            interopWindow.GetClassname(forceUpdate);
2✔
74
        }
75
        if ((retrieveSettings & InteropWindowRetrieveSettings.ProcessId) != 0)
2✔
76
        {
77
            interopWindow.GetProcessId(forceUpdate);
2✔
78
        }
79
        if ((retrieveSettings & InteropWindowRetrieveSettings.Parent) != 0)
2✔
80
        {
81
            interopWindow.GetParent(forceUpdate);
2✔
82
        }
83
        if ((retrieveSettings & InteropWindowRetrieveSettings.Visible) != 0)
2✔
84
        {
85
            interopWindow.IsVisible(forceUpdate);
2✔
86
        }
87
        if ((retrieveSettings | InteropWindowRetrieveSettings.Maximized) != 0)
2✔
88
        {
89
            interopWindow.IsMaximized(forceUpdate);
2✔
90
        }
91
        if ((retrieveSettings & InteropWindowRetrieveSettings.Minimized) != 0)
2✔
92
        {
93
            interopWindow.IsMinimized(forceUpdate);
2✔
94
        }
95
        if ((retrieveSettings & InteropWindowRetrieveSettings.ScrollInfo) != 0)
2✔
96
        {
97
            interopWindow.GetWindowScroller(forceUpdate: forceUpdate);
2✔
98
        }
99
        if ((retrieveSettings & InteropWindowRetrieveSettings.Children) != 0)
2!
100
        {
101
            interopWindow.GetChildren(forceUpdate);
×
102
        }
103
        if ((retrieveSettings & InteropWindowRetrieveSettings.ZOrderedChildren) != 0)
2!
104
        {
105
            interopWindow.GetZOrderedChildren(forceUpdate);
×
106
        }
107
        if ((retrieveSettings & InteropWindowRetrieveSettings.Placement) != 0)
2✔
108
        {
109
            interopWindow.GetPlacement(forceUpdate);
2✔
110
        }
111
        if ((retrieveSettings & InteropWindowRetrieveSettings.Text) != 0)
2✔
112
        {
113
            interopWindow.GetText(forceUpdate);
2✔
114
        }
115
        return interopWindow;
2✔
116
    }
117

118
    /// <summary>
119
    ///     Get the Windows caption (title)
120
    /// </summary>
121
    /// <param name="interopWindow">InteropWindow</param>
122
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
123
    /// <returns>string with the caption</returns>
124
    public static string GetCaption(this IInteropWindow interopWindow, bool forceUpdate = false)
125
    {
126
        if (interopWindow.Caption != null && !forceUpdate)
9!
127
        {
128
            return interopWindow.Caption;
×
129
        }
130

131
        // Calling User32Api.GetText (GetWindowText) for the current Process will hang, deadlock, this should be ignored
132
        if (interopWindow.IsOwnedByCurrentThread())
9!
133
        {
134
            // TODO: it might have a value, but can't get it. Returning null would be bad... so return empty
135
            interopWindow.Caption = string.Empty;
×
136
            Log.Warn().WriteLine("Do not call GetWindowText for a Window ({0}) which belongs the current thread! An empty string is returned.", interopWindow.Handle);
×
137
        }
138
        else
139
        {
140
            var caption = User32Api.GetText(interopWindow.Handle);
9✔
141
            interopWindow.Caption = caption;
9✔
142
        }
143
        return interopWindow.Caption;
9✔
144
    }
145

146
    /// <summary>
147
    ///     Get the children of the specified interopWindow, this is not lazy!
148
    /// </summary>
149
    /// <param name="interopWindow">InteropWindow</param>
150
    /// <param name="forceUpdate">True to force updating</param>
151
    /// <returns>IEnumerable with InteropWindow</returns>
152
    public static IEnumerable<IInteropWindow> GetChildren(this IInteropWindow interopWindow, bool forceUpdate = false)
153
    {
154
        if (interopWindow.HasChildren && !interopWindow.HasZOrderedChildren && !forceUpdate)
117✔
155
        {
156
            return interopWindow.Children;
1✔
157
        }
158
        interopWindow.HasZOrderedChildren = false;
116✔
159

160
        var children = new List<IInteropWindow>();
116✔
161
        // Store it in the Children property
162
        interopWindow.Children = children;
116✔
163
        foreach (var child in WindowsEnumerator.EnumerateWindows(interopWindow))
828✔
164
        {
165
            child.ParentWindow = interopWindow;
298✔
166
            children.Add(child);
298✔
167
        }
168
        return children;
116✔
169
    }
170

171
    /// <summary>
172
    ///     Get the Windows class name
173
    /// </summary>
174
    /// <param name="interopWindow">InteropWindow</param>
175
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
176
    /// <returns>string with the classname</returns>
177
    public static string GetClassname(this IInteropWindow interopWindow, bool forceUpdate = false)
178
    {
179
        if (interopWindow.Classname != null && !forceUpdate)
377✔
180
        {
181
            return interopWindow.Classname;
122✔
182
        }
183

184
        var className = User32Api.GetClassname(interopWindow.Handle);
255✔
185
        interopWindow.Classname = className;
255✔
186
        return interopWindow.Classname;
255✔
187
    }
188

189
    /// <summary>
190
    ///     Get the WindowInfo
191
    /// </summary>
192
    /// <param name="interopWindow">InteropWindow</param>
193
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
194
    /// <param name="autoCorrect">enable auto correction, e,g, have the bounds cropped to the parent(s)</param>
195
    /// <returns>WindowInfo</returns>
196
    public static WindowInfo GetInfo(this IInteropWindow interopWindow, bool forceUpdate = false, bool autoCorrect = true)
197
    {
198
        if (interopWindow.Info.HasValue && !forceUpdate)
121!
199
        {
200
            return interopWindow.Info.Value;
×
201
        }
202

203
        var windowInfo = WindowInfo.Create();
121✔
204
        User32Api.GetWindowInfo(interopWindow.Handle, ref windowInfo);
121✔
205

206
        // Test if we need to correct some values
207
        if (autoCorrect)
121✔
208
        {
209
            // Correct the bounds, for Windows 8+
210
            if (DwmApi.IsDwmEnabled)
121✔
211
            {
212
                // This only works for top level windows, otherwise a access denied is returned
213
                bool gotFrameBounds = DwmApi.GetExtendedFrameBounds(interopWindow.Handle, out var extendedFrameBounds);
121✔
214
                if (gotFrameBounds && (interopWindow.IsApp() || WindowsVersion.IsWindows10OrLater && !interopWindow.IsMaximized()))
121✔
215
                {
216
                    windowInfo.Bounds = extendedFrameBounds;
118✔
217
                }
218
            }
219

220
            var parentWindow = interopWindow.GetParentWindow();
121✔
221
            if (interopWindow.HasParent)
121✔
222
            {
223
                var parentInfo = parentWindow.GetInfo(forceUpdate, true);
40✔
224
                windowInfo.Bounds = windowInfo.Bounds.Intersect(parentInfo.Bounds);
40✔
225
                windowInfo.ClientBounds = windowInfo.ClientBounds.Intersect(parentInfo.ClientBounds);
40✔
226
            }
227
        }
228

229
        interopWindow.Info = windowInfo;
121✔
230
        return windowInfo;
121✔
231
    }
232

233
    /// <summary>
234
    ///     Get the parent
235
    /// </summary>
236
    /// <param name="interopWindow">InteropWindow</param>
237
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
238
    /// <returns>IntPtr for the parent</returns>
239
    public static IntPtr GetParent(this IInteropWindow interopWindow, bool forceUpdate = false)
240
    {
241
        if (interopWindow.Parent.HasValue && !forceUpdate)
152✔
242
        {
243
            return interopWindow.Parent.Value;
29✔
244
        }
245
        var parent = User32Api.GetParent(interopWindow.Handle);
123✔
246
        // Invalidate ParentWindow if the value changed or is IntPtr.Zero
247
        if (interopWindow.ParentWindow?.Handle != parent)
123✔
248
        {
249
            interopWindow.ParentWindow = null;
122✔
250
        }
251
        interopWindow.Parent = parent;
123✔
252
        return interopWindow.Parent.Value;
123✔
253
    }
254

255
    /// <summary>
256
    ///     Get the parent IInteropWindow
257
    /// </summary>
258
    /// <param name="interopWindow">InteropWindow</param>
259
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
260
    /// <returns>IInteropWindow for the parent</returns>
261
    public static IInteropWindow GetParentWindow(this IInteropWindow interopWindow, bool forceUpdate = false)
262
    {
263
        if (interopWindow.ParentWindow != null && !forceUpdate)
121!
264
        {
265
            return interopWindow.ParentWindow;
×
266
        }
267

268
        var parent = interopWindow.Parent ?? interopWindow.GetParent(forceUpdate);
121!
269
        interopWindow.ParentWindow = parent == IntPtr.Zero ? null : InteropWindowFactory.CreateFor(parent);
121✔
270
        return interopWindow.ParentWindow;
121✔
271
    }
272

273
    /// <summary>
274
    ///     Get the WindowPlacement
275
    /// </summary>
276
    /// <param name="interopWindow">InteropWindow</param>
277
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
278
    /// <returns>WindowPlacement</returns>
279
    public static WindowPlacement GetPlacement(this IInteropWindow interopWindow, bool forceUpdate = false)
280
    {
281
        if (interopWindow.Placement.HasValue && !forceUpdate)
2!
282
        {
283
            return interopWindow.Placement.Value;
×
284
        }
285
        var placement = WindowPlacement.Create();
2✔
286
        User32Api.GetWindowPlacement(interopWindow.Handle, ref placement);
2✔
287
        interopWindow.Placement = placement;
2✔
288
        return interopWindow.Placement.Value;
2✔
289
    }
290

291
    /// <summary>
292
    ///     Get the process and thread which the specified window belongs to, the value is cached into the ProcessId of the WindowInfo
293
    /// </summary>
294
    /// <param name="interopWindow">InteropWindow</param>
295
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
296
    /// <returns>uint with process Id</returns>
297
    public static int GetProcessId(this IInteropWindow interopWindow, bool forceUpdate = false)
298
    {
299
        if (interopWindow.ProcessId.HasValue && !forceUpdate)
11!
300
        {
UNCOV
301
            return interopWindow.ProcessId.Value;
×
302
        }
303

304
        var threadId = User32Api.GetWindowThreadProcessId(interopWindow.Handle, out var processId);
11✔
305
        interopWindow.ThreadId = threadId;
11✔
306
        interopWindow.ProcessId = processId;
11✔
307
        return interopWindow.ProcessId.Value;
11✔
308
    }
309

310
    /// <summary>
311
    ///     Get the region for a window
312
    /// </summary>
313
    /// <param name="interopWindow">InteropWindow</param>
314
    public static Region GetRegion(this IInteropWindow interopWindow)
315
    {
316
        using (var region = Gdi32Api.CreateRectRgn(0, 0, 0, 0))
×
317
        {
318
            if (region.IsInvalid)
×
319
            {
320
                return null;
×
321
            }
322
            var result = User32Api.GetWindowRgn(interopWindow.Handle, region);
×
323
            if (result != RegionResults.Error && result != RegionResults.NullRegion)
×
324
            {
325
                return Region.FromHrgn(region.DangerousGetHandle());
×
326
            }
327
        }
×
328
        return null;
×
329
    }
×
330

331
    /// <summary>
332
    ///     Get text from the window
333
    /// </summary>
334
    /// <param name="interopWindow">InteropWindow</param>
335
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
336
    /// <returns>string with the text</returns>
337
    public static string GetText(this IInteropWindow interopWindow, bool forceUpdate = false)
338
    {
339
        if (interopWindow.Text != null && !forceUpdate)
2!
340
        {
341
            return interopWindow.Text;
×
342
        }
343
        var text = User32Api.GetTextFromWindow(interopWindow.Handle);
2✔
344
        interopWindow.Text = text;
2✔
345
        return text;
2✔
346
    }
347

348
    /// <summary>
349
    ///     Extension method to create a WindowScroller
350
    /// </summary>
351
    /// <param name="interopWindow">IInteropWindow</param>
352
    /// <param name="scrollBarType">ScrollBarTypes</param>
353
    /// <param name="forceUpdate">true to force a retry, even if the previous check failed</param>
354
    /// <returns>WindowScroller or null</returns>
355
    public static WindowScroller GetWindowScroller(this IInteropWindow interopWindow, ScrollBarTypes scrollBarType = ScrollBarTypes.Vertical, bool forceUpdate = false)
356
    {
357
        if (!forceUpdate && interopWindow.CanScroll.HasValue && !interopWindow.CanScroll.Value)
2!
358
        {
359
            return null;
×
360
        }
361
        var initialScrollInfo = ScrollInfo.Create(ScrollInfoMask.All);
2✔
362
        if (User32Api.GetScrollInfo(interopWindow.Handle, scrollBarType, ref initialScrollInfo) && initialScrollInfo.Minimum != initialScrollInfo.Maximum)
2!
363
        {
364
            var windowScroller = new WindowScroller
×
365
            {
×
366
                ScrollingWindow = interopWindow,
×
367
                ScrollBarWindow = interopWindow,
×
368
                ScrollBarType = scrollBarType,
×
369
                InitialScrollInfo = initialScrollInfo,
×
370
                WheelDelta = (int) (120 * (initialScrollInfo.PageSize / WindowScroller.ScrollWheelLinesFromRegistry))
×
371
            };
×
372
            interopWindow.CanScroll = true;
×
373
            return windowScroller;
×
374
        }
375
        if (User32Api.GetScrollInfo(interopWindow.Handle, ScrollBarTypes.Control, ref initialScrollInfo) && initialScrollInfo.Minimum != initialScrollInfo.Maximum)
2!
376
        {
377
            var windowScroller = new WindowScroller
×
378
            {
×
379
                ScrollingWindow = interopWindow,
×
380
                ScrollBarWindow = interopWindow,
×
381
                ScrollBarType = ScrollBarTypes.Control,
×
382
                InitialScrollInfo = initialScrollInfo,
×
383
                WheelDelta = (int) (120 * (initialScrollInfo.PageSize / WindowScroller.ScrollWheelLinesFromRegistry))
×
384
            };
×
385
            interopWindow.CanScroll = true;
×
386
            return windowScroller;
×
387
        }
388
        interopWindow.CanScroll = false;
2✔
389
        return null;
2✔
390
    }
391

392
    /// <summary>
393
    ///     Get the children of the specified interopWindow, from top to bottom. This is not lazy
394
    ///     This might get different results than the GetChildren
395
    /// </summary>
396
    /// <param name="interopWindow">InteropWindow</param>
397
    /// <param name="forceUpdate">True to force updating</param>
398
    /// <returns>IEnumerable with InteropWindow</returns>
399
    public static IEnumerable<IInteropWindow> GetZOrderedChildren(this IInteropWindow interopWindow, bool forceUpdate = false)
400
    {
401
        if (interopWindow.HasChildren && interopWindow.HasZOrderedChildren && !forceUpdate)
×
402
        {
403
            return interopWindow.Children;
×
404
        }
405
        interopWindow.HasZOrderedChildren = true;
×
406

407
        var children = new List<IInteropWindow>();
×
408
        // Store it in the Children property
409
        interopWindow.Children = children;
×
410
        foreach (var child in InteropWindowQuery.GetTopWindows(interopWindow))
×
411
        {
412
            child.ParentWindow = interopWindow;
×
413
            children.Add(child);
×
414
        }
415
        return children;
×
416
    }
417

418
    /// <summary>
419
    ///     Returns if the IInteropWindow is docked to the left of the other IInteropWindow
420
    /// </summary>
421
    /// <param name="window1">IInteropWindow</param>
422
    /// <param name="window2">IInteropWindow</param>
423
    /// <param name="retrieveBoundsFunc">Function which returns the bounds for the IInteropWindow</param>
424
    /// <returns>bool true if docked</returns>
425
    public static bool IsDockedToLeftOf(this IInteropWindow window1, IInteropWindow window2, Func<IInteropWindow, NativeRect> retrieveBoundsFunc = null)
426
    {
427
        retrieveBoundsFunc ??= (window => window.GetInfo().Bounds);
×
428
        return retrieveBoundsFunc(window1).IsDockedToLeftOf(retrieveBoundsFunc(window2));
×
429
    }
430

431
    /// <summary>
432
    ///     Returns if the IInteropWindow is docked to the left of the other IInteropWindow
433
    /// </summary>
434
    /// <param name="window1">IInteropWindow</param>
435
    /// <param name="window2">IInteropWindow</param>
436
    /// <param name="retrieveBoundsFunc">Function which returns the bounds for the IInteropWindow</param>
437
    /// <returns>bool true if docked</returns>
438
    public static bool IsDockedToRightOf(this IInteropWindow window1, IInteropWindow window2, Func<IInteropWindow, NativeRect> retrieveBoundsFunc = null)
439
    {
440
        retrieveBoundsFunc ??= (window => window.GetInfo().Bounds);
×
441
        return retrieveBoundsFunc(window1).IsDockedToRightOf(retrieveBoundsFunc(window2));
×
442
    }
443

444
    /// <summary>
445
    ///     Retrieve if the window is maximized (Iconic)
446
    /// </summary>
447
    /// <param name="interopWindow">InteropWindow</param>
448
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
449
    /// <returns>bool true if Iconic (minimized)</returns>
450
    public static bool IsMaximized(this IInteropWindow interopWindow, bool forceUpdate = false)
451
    {
452
        if (!interopWindow.IsMaximized.HasValue || forceUpdate)
102✔
453
        {
454
            interopWindow.IsMaximized = User32Api.IsZoomed(interopWindow.Handle);
102✔
455
        }
456
        return interopWindow.IsMaximized.Value;
102✔
457
    }
458

459
    /// <summary>
460
    ///     Retrieve if the window is minimized (Iconic)
461
    /// </summary>
462
    /// <param name="interopWindow">InteropWindow</param>
463
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
464
    /// <returns>bool true if Iconic (minimized)</returns>
465
    public static bool IsMinimized(this IInteropWindow interopWindow, bool forceUpdate = false)
466
    {
467
        if (!interopWindow.IsMinimized.HasValue || forceUpdate)
3✔
468
        {
469
            interopWindow.IsMinimized = User32Api.IsIconic(interopWindow.Handle);
3✔
470
        }
471
        return interopWindow.IsMinimized.Value;
3✔
472
    }
473

474
    /// <summary>
475
    ///     Retrieve if the window is Visible and not cloaked (different desktop)
476
    /// </summary>
477
    /// <param name="interopWindow">InteropWindow</param>
478
    /// <param name="forceUpdate">set to true to make sure the value is updated</param>
479
    /// <returns>bool true if minimizedIconic (minimized)</returns>
480
    public static bool IsVisible(this IInteropWindow interopWindow, bool forceUpdate = false)
481
    {
482
        if (!interopWindow.IsVisible.HasValue || forceUpdate)
7✔
483
        {
484
            interopWindow.IsVisible = User32Api.IsWindowVisible(interopWindow.Handle) && !DwmApi.IsWindowCloaked(interopWindow.Handle);
7✔
485
        }
486
        return interopWindow.IsVisible.Value;
7✔
487
    }
488

489
    /// <summary>
490
    ///     Test if the window is owned by the current process
491
    /// </summary>
492
    /// <param name="interopWindow">InteropWindow</param>
493
    /// <returns>bool true if the window is owned by the current process</returns>
494
    public static bool IsOwnedByCurrentProcess(this IInteropWindow interopWindow)
495
    {
496
        return Kernel32Api.GetCurrentProcessId() == interopWindow.GetProcessId();
×
497
    }
498

499
    /// <summary>
500
    ///     Test if the window is owned by the current thread
501
    /// </summary>
502
    /// <param name="interopWindow">InteropWindow</param>
503
    /// <returns>bool true if the window is owned by the current thread</returns>
504
    public static bool IsOwnedByCurrentThread(this IInteropWindow interopWindow)
505
    {
506
        // Although the process id is returned, the following also reads the Thread-ID
507
        interopWindow.GetProcessId();
9✔
508
        return Kernel32Api.GetCurrentThreadId() == interopWindow.ThreadId;
9✔
509
    }
510

511
    /// <summary>
512
    ///     Maximize the window
513
    /// </summary>
514
    /// <param name="interopWindow">InteropWindow</param>
515
    /// <returns>IInteropWindow for fluent calls</returns>
516
    public static IInteropWindow Maximize(this IInteropWindow interopWindow)
517
    {
518
        User32Api.ShowWindow(interopWindow.Handle, ShowWindowCommands.Maximize);
×
519
        interopWindow.IsMaximized = true;
×
520
        interopWindow.IsMinimized = false;
×
521
        return interopWindow;
×
522
    }
523

524
    /// <summary>
525
    ///     Minimize the Window
526
    /// </summary>
527
    /// <param name="interopWindow">InteropWindow</param>
528
    /// <returns>IInteropWindow for fluent calls</returns>
529
    public static IInteropWindow Minimize(this IInteropWindow interopWindow)
530
    {
531
        User32Api.ShowWindow(interopWindow.Handle, ShowWindowCommands.Minimize);
×
532
        interopWindow.IsMinimized = true;
×
533
        return interopWindow;
×
534
    }
535

536
    /// <summary>
537
    ///     Restore (Un-Minimize/Maximize) the Window
538
    /// </summary>
539
    /// <param name="interopWindow">InteropWindow</param>
540
    /// <returns>IInteropWindow for fluent calls</returns>
541
    public static IInteropWindow Restore(this IInteropWindow interopWindow)
542
    {
543
        User32Api.ShowWindow(interopWindow.Handle, ShowWindowCommands.Restore);
×
544
        interopWindow.IsMinimized = false;
×
545
        interopWindow.IsMaximized = false;
×
546
        return interopWindow;
×
547
    }
548

549
    /// <summary>
550
    ///     Set the Extended WindowStyle
551
    /// </summary>
552
    /// <param name="interopWindow">InteropWindow</param>
553
    /// <param name="extendedWindowStyleFlags">ExtendedWindowStyleFlags</param>
554
    /// <returns>IInteropWindow for fluent calls</returns>
555
    public static IInteropWindow SetExtendedStyle(this IInteropWindow interopWindow, ExtendedWindowStyleFlags extendedWindowStyleFlags)
556
    {
557
        User32Api.SetExtendedWindowStyle(interopWindow.Handle, extendedWindowStyleFlags);
×
558
        interopWindow.Info = null;
×
559
        return interopWindow;
×
560
    }
561

562
    /// <summary>
563
    ///     Set the WindowStyle
564
    /// </summary>
565
    /// <param name="interopWindow">InteropWindow</param>
566
    /// <param name="windowStyleFlags">WindowStyleFlags</param>
567
    /// <returns>IInteropWindow for fluent calls</returns>
568
    public static IInteropWindow SetStyle(this IInteropWindow interopWindow, WindowStyleFlags windowStyleFlags)
569
    {
570
        User32Api.SetWindowStyle(interopWindow.Handle, windowStyleFlags);
×
571
        interopWindow.Info = null;
×
572
        return interopWindow;
×
573
    }
574

575
    /// <summary>
576
    ///     Set the WindowPlacement
577
    /// </summary>
578
    /// <param name="interopWindow">InteropWindow</param>
579
    /// <param name="placement">WindowPlacement</param>
580
    /// <returns>IInteropWindow for fluent calls</returns>
581
    public static IInteropWindow SetPlacement(this IInteropWindow interopWindow, WindowPlacement placement)
582
    {
583
        User32Api.SetWindowPlacement(interopWindow.Handle, ref placement);
×
584
        interopWindow.Placement = placement;
×
585
        return interopWindow;
×
586
    }
587

588
    /// <summary>
589
    ///     Set the window as foreground window
590
    /// </summary>
591
    /// <param name="interopWindow">The window to bring to the foreground</param>
592
    public static async ValueTask ToForegroundAsync(this IInteropWindow interopWindow)
593
    {
594
        // Nothing we can do if it's not visible!
595
        if (!interopWindow.IsVisible())
×
596
        {
597
            return;
×
598
        }
599

600
        var foregroundWindow = User32Api.GetForegroundWindow();
×
601
        // Window is already the foreground window
602
        if (foregroundWindow == interopWindow.Handle)
×
603
        {
604
            return;
×
605
        }
606
        if (interopWindow.IsMinimized())
×
607
        {
608
            interopWindow.Restore();
×
609
            while (interopWindow.IsMinimized())
×
610
            {
611
                await Task.Delay(50).ConfigureAwait(false);
×
612
            }
613
        }
614

615
        // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx
616
        // It was advised to use the menu (ALT) key, but this was a solution which Jan Karger showed me
617

618
        var threadId1 = User32Api.GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero);
×
619
        var threadId2 = User32Api.GetWindowThreadProcessId(interopWindow.Handle, IntPtr.Zero);
×
620

621
        // Show window in foreground.
622
        if (threadId1 != threadId2)
×
623
        {
624
            User32Api.AttachThreadInput(threadId1, threadId2, 1);
×
625
            User32Api.SetForegroundWindow(interopWindow.Handle);
×
626
            User32Api.AttachThreadInput(threadId1, threadId2, 0);
×
627
        }
628
        else
629
        {
630
            User32Api.SetForegroundWindow(interopWindow.Handle);
×
631
        }
632

633
        // Show window in foreground.
634
        User32Api.BringWindowToTop(interopWindow.Handle);
×
635
        User32Api.SetForegroundWindow(interopWindow.Handle);
×
636
    }
×
637

638
    /// <summary>
639
    /// Move the specified window to a new location
640
    /// </summary>
641
    /// <param name="interopWindow">IInteropWindow</param>
642
    /// <param name="location">NativePoint with the offset</param>
643
    /// <returns>IInteropWindow for fluent calls</returns>
644
    public static IInteropWindow MoveTo(this IInteropWindow interopWindow, NativePoint location)
645
    {
646
        User32Api.SetWindowPos(interopWindow.Handle, IntPtr.Zero, location.X, location.Y, 0, 0, WindowPos.SWP_NOSIZE | WindowPos.SWP_SHOWWINDOW | WindowPos.SWP_NOACTIVATE | WindowPos.SWP_NOZORDER);
×
647
        interopWindow.Info = null;
×
648
        return interopWindow;
×
649
    }
650

651
    /// <summary>
652
    /// Get all the other windows belonging to the process which owns the specified window
653
    /// </summary>
654
    /// <param name="windowToLinkTo">IInteropWindow</param>
655
    /// <returns>IEnumerable of IInteropWindow</returns>
656
    public static IEnumerable<IInteropWindow> GetLinkedWindows(this IInteropWindow windowToLinkTo)
657
    {
658
        int processIdSelectedWindow = windowToLinkTo.GetProcessId();
×
659
        return InteropWindowQuery.GetTopLevelWindows().Where(window => window.Handle != windowToLinkTo.Handle && window.GetProcessId() == processIdSelectedWindow);
×
660
    }
661

662
    /// <summary>
663
    ///     Get a location where this window would be visible
664
    ///     * if none is found return false, formLocation = the original location
665
    ///     * if something is found, return true and formLocation = new location
666
    /// </summary>
667
    /// <param name="interopWindow">IInteropWindow, the window to find a location for</param>
668
    /// <param name="formLocation">NativePoint with the location where the window will fit</param>
669
    /// <returns>true if a location if found, and the formLocation is also set</returns>
670
    public static bool GetVisibleLocation(this IInteropWindow interopWindow, out NativePoint formLocation)
671
    {
672
        bool doesWindowFit = false;
×
673
        var windowRectangle = interopWindow.GetInfo().Bounds;
×
674
        // assume own location
675
        formLocation = windowRectangle.Location;
×
676
        var primaryDisplay = DisplayInfo.AllDisplayInfos.First(x => x.IsPrimary);
×
677
        using (var workingArea = new Region(primaryDisplay.Bounds))
×
678
        {
679
            // Create a region with the screens working area
680
            foreach (var display in DisplayInfo.AllDisplayInfos)
×
681
            {
682
                if (!display.IsPrimary)
×
683
                {
684
                    workingArea.Union(display.Bounds);
×
685
                }
686
            }
687

688
            // If the formLocation is not inside the visible area
689
            if (!workingArea.AreRectangleCornersVisisble(windowRectangle))
×
690
            {
691
                // If none found we find the biggest screen
692
                foreach (var display in DisplayInfo.AllDisplayInfos)
×
693
                {
694
                    var newWindowRectangle = new Rectangle(display.WorkingArea.Location, windowRectangle.Size);
×
695
                    if (!workingArea.AreRectangleCornersVisisble(newWindowRectangle))
×
696
                    {
697
                        continue;
698
                    }
699
                    formLocation = display.Bounds.Location;
×
700
                    doesWindowFit = true;
×
701
                    break;
×
702
                }
703
            }
704
            else
705
            {
706
                doesWindowFit = true;
×
707
            }
708
        }
×
709
        return doesWindowFit;
×
710
    }
711

712
    /// <summary>
713
    /// Return an Image representing the Window!
714
    /// As GDI+ draws it, it will be without Aero borders!
715
    /// </summary>
716
    public static TBitmap PrintWindow<TBitmap>(this IInteropWindow interopWindow) where TBitmap : class
717
    {
718
        var windowRect = interopWindow.GetInfo().Bounds;
×
719
        // Start the capture
720
        Exception exceptionOccured = null;
×
721
        Bitmap printWindowBitmap;
722
        using (var region = interopWindow.GetRegion())
×
723
        {
724
            var pixelFormat = PixelFormat.Format24bppRgb;
×
725
            // Only use 32 bpp ARGB when the window has a region
726
            if (region != null)
×
727
            {
728
                pixelFormat = PixelFormat.Format32bppArgb;
×
729
            }
730
            printWindowBitmap = new Bitmap(windowRect.Width, windowRect.Height, pixelFormat);
×
731
            using (var graphics = Graphics.FromImage(printWindowBitmap))
×
732
            {
733
                using (var graphicsDc = graphics.GetSafeDeviceContext())
×
734
                {
735
                    bool printSucceeded = User32Api.PrintWindow(interopWindow.Handle, graphicsDc.DangerousGetHandle(), PrintWindowFlags.PW_COMPLETE);
×
736
                    if (!printSucceeded)
×
737
                    {
738
                        // something went wrong, most likely a "0x80004005" (Acess Denied) when using UAC
739
                        exceptionOccured = new Win32Exception();
×
740
                    }
741
                }
×
742

743
                // Apply the region "transparency"
744
                if (region != null && !region.IsEmpty(graphics))
×
745
                {
746
                    graphics.ExcludeClip(region);
×
747
                    graphics.Clear(Color.Transparent);
×
748
                }
749

750
                graphics.Flush();
×
751
            }
×
752
        }
753

754
        // Return null if error
755
        if (exceptionOccured != null)
×
756
        {
757
            Log.Error().WriteLine("Error calling print window: {0}", exceptionOccured.Message);
×
758
            printWindowBitmap.Dispose();
×
759
            return default;
×
760
        }
761
        if (typeof(TBitmap).IsAssignableFrom(typeof(Bitmap)))
×
762
        {
763
            return printWindowBitmap as TBitmap;
×
764
        }
765
#if !NETSTANDARD2_0
766
        try
767
        {
768
            return printWindowBitmap.ToBitmapSource() as TBitmap;
×
769
        }
770
        finally
771
        {
772
            printWindowBitmap.Dispose();
×
773
        }
×
774
#else
775
            return default;
776
#endif
777
    }
×
778
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc