Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Would you mind merging my recent code update to window-finder-control? #1

Open
wants to merge 80 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
819715d
Upgrade target .NET framework to 3.5(for VS2015).
chjfth Jun 17, 2021
db5ff5d
Win32.cs: Fix WinAPI DllImport statements, according to pinvoke.net, …
chjfth Jun 17, 2021
4d07783
Important FIX: In picTarget_MouseMove(), code logic recursive calling…
chjfth Jun 17, 2021
1e94d65
Add VSPG facility to ease remote-deploying and debugging.
chjfth Jun 21, 2021
0f2b193
fix to prev commit
chjfth Jun 21, 2021
fac018f
Remove useless GetWindowRgn() call.
chjfth Jun 21, 2021
7a19552
Add missing app.config .
chjfth Jun 21, 2021
b98a359
HighlightWindow() : Win10.1607 mix-mode DPI environment adjustment ok.
chjfth Jun 21, 2021
4b8dc9c
Remove recent debug code.
chjfth Jun 21, 2021
7201869
Key comments in HighlightWindow().
chjfth Jun 21, 2021
e9dc644
New feature:
chjfth Jun 21, 2021
4c2cad3
Refactoring. Preparing for new "aiming-frame" option.
chjfth Jun 23, 2021
1d9904b
Add new target-window highlighting method called "Aiming-frame", now …
chjfth Jun 23, 2021
f9f079f
Update: The red aiming-window is now placed JUST ABOVE the target HWN…
chjfth Jun 23, 2021
74b63e0
Test App UI: Add option to choose between two highlight method.
chjfth Jun 23, 2021
ce59a5e
some temp debugging code
chjfth Jun 23, 2021
3ec9d9e
TestApp: Add UI label to show current process DPI awareness.
chjfth Jun 24, 2021
96ded9b
TestApp UI: Show GetWindowRect() value from caller's perspective.
chjfth Jun 25, 2021
a254694
TestApp: make edit box a bit wider
chjfth Jun 25, 2021
5e58b52
New feature: Pass a parameter to assign DPI awareness for this program.
chjfth Jun 25, 2021
af2d92e
dpiAware-PerMon.manifest
chjfth Jun 25, 2021
9895fc2
Add exe samples of different manifests.
chjfth Jun 25, 2021
d232c0d
HighlightWindow_InvertColor(): Fix a buggy situation on Win81: SysDpi…
chjfth Jun 25, 2021
5168a45
A minor update: Using a enum parameter with default value, causes
chjfth Jun 25, 2021
18f498f
Some test code.
chjfth Jun 25, 2021
3117ee6
Update sample exe, not BtnLook show exe name on window title.
chjfth Jun 26, 2021
8212104
Show HWND value at window title as well.
chjfth Jun 26, 2021
5daced8
Clean up some debugging code.
chjfth Jun 27, 2021
2fb6f0a
!! Found a solution for a hard problem for Win7. Aiming-frame did not…
chjfth Jun 27, 2021
43e7a02
Comment on previous commit.
chjfth Jun 27, 2021
ed3a06c
In HighlightWindow_Overlaying(), remove a piece of unnecessary code f…
chjfth Jun 27, 2021
8997163
supp to prev commit
chjfth Jun 27, 2021
dc7c5bc
Trying to show target window *physical* width & height along with the…
chjfth Jun 27, 2021
69b3784
minor refactor
chjfth Jun 27, 2021
74d9626
Supp to previous commit. Win7 self DPI-unaware now reports correct *p…
chjfth Jun 28, 2021
9e05176
Update to previous commit: Win10.1607+, now aming-frame always report…
chjfth Jun 28, 2021
2f2d2ee
For Win81: Aiming-frame will prompt for inaccurate physical coords wi…
chjfth Jun 28, 2021
0d4575c
a comment for previous commit: For Win81,
chjfth Jun 28, 2021
377b9f6
New feature: Capture screen image on target window location to clipbo…
chjfth Jun 28, 2021
b4bb1be
Add UI for "Capture to clipboard".
chjfth Jun 28, 2021
d2835c0
Add tooltip to Screenshot checkbox.
chjfth Jun 28, 2021
cf05fa5
Manually update TabIndex, not ideal yet. I'd like it to rest on the w…
chjfth Jun 28, 2021
ee3dba4
Experiment: For Invert-color method, I tried to turn OFF DWM non-clie…
chjfth Jun 28, 2021
9895e9e
Minor UI update: Screenshot checkbox now workable with keyboard shor…
chjfth Jul 2, 2021
5f57b7d
var rename: ckbClipboard -> ckbScreenshot
chjfth Jul 2, 2021
54732db
Implement previous commit by by deriving `class MyCheckBox`.
chjfth Jul 2, 2021
880d0bf
Rename: isCaptureToClipboard -> isDoScreenshot
chjfth Jul 2, 2021
4c0f5e9
Add comments for Win7_SpecialAdjust().
chjfth Jul 2, 2021
ee406ee
Big update: IsRelativeWindow() -> IsIncludeWindow().
chjfth Jul 2, 2021
edb3a57
comment update
chjfth Jul 2, 2021
f64568f
minor change to avoid lots of ArgumentOutOfRangeException dumped to d…
chjfth Jul 2, 2021
e19cd7a
Update: Add code to reduce aiming-frame flickering.
chjfth Jul 2, 2021
c951b6a
Add tooltips.
chjfth Jul 2, 2021
9fcf9a1
minor: Disable ESC closing the whole dialog.
chjfth Jul 3, 2021
9b97b10
Adjust TestApp window size to 400x240 (on a default Win7) to ease dem…
chjfth Jul 3, 2021
bd28b2d
minor refactoring
chjfth Jul 3, 2021
cdf946b
TestApp: Remove useless var: windowHandleChanging
chjfth Jul 3, 2021
1c7f68d
TestApp new feature: It shows system-reported mouse position(as retur…
chjfth Jul 3, 2021
bcaf365
TestApp optimize: windowFinder.WindowHandleChanged event is fired onl…
chjfth Jul 3, 2021
ba266c7
minor fix to make it runnable on WinXP.
chjfth Jul 4, 2021
988e2b7
MessageBox warn about program misbehavior on Win81.
chjfth Jul 4, 2021
256d761
Rename: Win7_SpecialAdjust() -> Win7_HighDpiAdjustRect()
chjfth Jul 5, 2021
6f0e9ed
RENAME: AimingFrame -> SnapFrame
chjfth Jul 10, 2021
bec8dde
Rename: WindowFinder -> wcFinder.
chjfth Jul 10, 2021
1615d59
typo fix
chjfth Jul 10, 2021
bb79ab8
Update README for new version.
chjfth Jul 10, 2021
fa7b4a3
Commit recent binary file.
chjfth Jul 10, 2021
66fd2e2
BtnLook screenshot sample.
chjfth Jul 10, 2021
657000c
BtnLook code reference.
chjfth Jul 11, 2021
5486c99
minor comment
chjfth Jul 12, 2021
4a1f497
Fix a bug for Win81.
chjfth Jul 12, 2021
e645973
Fix: Catch exception from Clipboard.SetImage(), to avoid program crash.
chjfth Jul 12, 2021
b20cd57
Win81 minor update: for non-Permon-aware caller on multi-monitor env,…
chjfth Jul 12, 2021
fedf48c
Add control version number 2.1, TestApp version number 10.
chjfth Jul 12, 2021
dfa3acb
Update binary to v2.1.10 .
chjfth Jul 12, 2021
56ec73c
minor comment and rename
chjfth Jul 18, 2021
59e4f5c
Add tooltip for WinRect info.
chjfth Jul 18, 2021
f68e5b0
Fix for Win81:
chjfth Jul 18, 2021
4b31444
Add Techinfo.md, telling some details of wcFinder's code.
chjfth Jul 18, 2021
2a9f68a
Tech info: Windows 7 non-100% system DPI, ghost window problem.
chjfth Jul 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed Bin/WindowFinder.zip
Binary file not shown.
Binary file added Bin/wcFinder.zip
Binary file not shown.
Binary file added Graphics/Screenshots/Screenshot-Win10-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Graphics/Screenshots/Screenshot-Win10-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Graphics/Screenshots/Win7-ghost-window-out.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Graphics/Screenshots/Win7-not-ghost-window-in.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Copyright (c) 2021, Jimm Chen <chjfth@gmail.com>
Copyright (c) 2014, Joe Esposito <joe@joeyespo.com>

Permission to use, copy, modify, and/or distribute this software for any
Expand Down
54 changes: 46 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,57 @@
Window Finder Control
---------------------
Window Finder Control (wcFinder)
--------------------------------

A .NET control that behaves like the Spy++ window finder tool, allowing the
user to select a top-level window by dragging a crosshair icon over it.
user to select a top-level window or child window by dragging a crosshair icon over it.

In June 2021, wcFinder receives a major upgrade with the following features:

* Introduce a new target-window highlighting method called **Snap-frame**.
This new method works with Win7+ DWM-rendered top-level window,
while the old method(called **Invert-color** as used by Spy++) does not.
* wcFinder is now fully DPI-scaling aware. With the new Snap-frame method, it works with
Windows XP up to Windows 10(1607+).
* wcFinder itself can be run in all three DPI-awareness context: DPI-unaware, System-DPI-aware
and Per-monitor-DPI-aware.
* The target-window can also be in any of the three DPI-awareness context.
* DPI-awareness cross-matching is supported. For example, you run wcFinder DPI-unware, and
it can highlight System-DPI-aware target-window correctly.

Note: the Invert-color method is too old and inevitably buggy in various high-DPI scaling environments.
Although wcFinder has made quite many efforts to make it work across multiple situations, it is pity
to say we cannot guarantee its correctness. No wonder Visual Studio 2019-bundled Spy++ does not fix it until now.

Screenshots
-----------

![Window Finder Control: Screenshot 1](Graphics/Screenshots/Screenshot-01.png)
![Window Finder Control: Screenshot 1](Graphics/Screenshots/Screenshot-Win10-01.png)

The wcFinder TestApp with a wcFinder control in its UI.


![Window Finder Control: Screenshot 2](Graphics/Screenshots/Screenshot-Win10-02.png)

The wcFinder control in action! The crosshair is being dragged over an "Open" window from Notepad.
The target-window is surrounded/highlighted with a red Snap-frame, and information about current target-window
is displayed on TestApp's UI.

Usage Hints
-----------

To launch wcFinder TestApp itself in different DPI-awareness context, pass parameter 0, 1, or 2 .
The effective DPI-awareness value will be displayed at first line of the TestApp UI.

When the currently aimed target-window is a child window, you can press Ctrl to change the target
to its top-level window.

On TestApp UI, when Screenshot checkbox is ticked, the screen area of the confirmed target-window will be
screenshot to Clipboard. You can use a tool like [Free Clipboard Viewer](https://freeclipboardviewer.com/)
to preview the screenshot. Note that this an extra feature from the TestApp, not the core feature provided
by wcFinder control.

The test window for the control.

Some technical info
===================

![Window Finder Control: Screenshot 2](Graphics/Screenshots/Screenshot-02.png)
[Check this page.](Techinfo.md)

The window finder control in action! The crosshair is being dragged over
Calculator, which is highlighted, showing its class, text, and charset.
82 changes: 82 additions & 0 deletions Techinfo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
## Virtual coordinate vs Physical coordinate, of the Snap-frame.

* Virtual coordinate: The RECT value as returned by calling `GetWindowRect()`.
* Physical coordinate: The RECT value as measured by actual screen pixel.

Due to DPI-virtualization behavior for DPI-unaware and SysDpi-aware programs, such a program can see different Virtual coordinate and Physical coordinate for a specific HWND.

In order to show Physical width of the red Snap-frame(at left-top corner), *different* solutions should be applied for *different* Windows versions.

### Case 1: Windows 10.1607+

The solution is concise and clear, because Microsoft has finally supplied abundant APIs for mixmode DPI environment.

To get Physical coordinate of an HWND, the caller just temporarily switch itself to Per-monitor-aware context(`SetThreadDpiAwarenessContext`) before calling GetWindowRect(), and GetWindowRect returns exact physical coordinate.

### Case 2: Windows 7

We know that Win7 has the most fundamental high-DPI feature(all monitors must have consistent DPI scaling) and the least high-DPI API support, only `SetProcessDPIAware()` and `IsProcessDPIAware()` available.

There is a tough situation: When DPI-scaling is set to e.g. 150%, and, for example:

1. Our program is DPI-unaware.
2. There is another a DPI-unaware program with window A, and our program calling `GetWindowRect(hwndA)` returns virtual width 500. The window A is bitmap-stretched to 150% on physical screen so it actually has physical width 750.
3. There is a SysDpi-aware program with window B, and our program calling `GetWindowRect(hwndB)` also returns virtual width 500 . The window B is NOT bitmap-stretched so its physical width is 500 as well.

Then how can our program correctly deduce the widths of hwndA and hwndB? (In case our program is SysDpi-aware, the same problem exists.)

Windows 7 lacks API like `GetDpiForWindow()` for this purpose, and there is no Per-monitor-aware mode on Win7, so I call it tough.

As shown in the following figures, BtnLook0-unaware and BtnLook1-unaware has exactly the same program code, except that the latter is marked `<dpiAware>true</dpiAware>` in its manifest.

![The DPI-unaware target-window has 150% width.](Graphics/Screenshots/Screenshot-Win7-highdpi-BtnLook0.png)

![The SysDpi-aware target-window has 100% width.](Graphics/Screenshots/Screenshot-Win7-highdpi-BtnLook1.png)

Fortunately, I have found a creative way to cope with it. Win7 has an API named `DwmGetWindowAttribute` that definitely reports **physical** coordinate of a top-level HWND. With the help of it, hwndA and hwndB's physical widths can be distinguished.

### Case 3: Windows 8.1 (including Windows 10 pre-1607)

Windows 8.1 is truely problematic. It has rudimentary *mixmode high-DPI* feature, i.e., it can choose different DPI-scaling for different monitor for us, but, it still lacks per-window API like `GetDpiForWindow`.

So, when the following two conditions are both met, physical coordinate of a target window CANNOT be reliably deduced. The wcFinder TestApp will show a question mark in such situation.

* Condition 1: wcFinder program itself is NOT Per-monitor-aware.
* Condition 2: Win81 has more than one monitor.

For example, monitor #1(primary) is 1024x768 with 100% scaling, monitor 2(secondary) is 1280*1024 with 125% scaling. Physical width of BtnLook0-unaware will be different when its window is on monitor #1 vs on monitor #2. However, DPI-unaware wcFinder will always deduce the same physical width for BtnLook window no matter which monitor BtnLook resides.

![Win81-400px-scaling100pct](Graphics/Screenshots/Win81-400px-scaling100pct.png)

![Win81-500px-scaling125pct](Graphics/Screenshots/Win81-500px-scaling125pct.png)

Frankly, I have not found a way to cope with this situation on Win81.


## Windows 7 non-100% system DPI, ghost window problem

When the following three are true on Win7,

1. System DPI is non-100%, 150% for example.
2. wcFinder is DPI-unaware program.
3. Target window is an DPI-unaware program(can be wcFinder itself) which is scaled up to 150% on physical screen.

Symptom: When the crosshair is dragged over some position left-and-top to the target window(outside target window), the target window or some of its child-window is mysteriously highlighted, as if there is a ghost window at the undesired location.

This is a BUG of Windows 7, in API `ClientToScreen()`, which has been fixed in Windows 10, at least I see it gone on Win10.1909.

The ghost window is at the imagined 100%-scaling location of the target window. Why this happens? Observing the "Mouse at screen" X, Y value reported by TestApp, we can see:
* When the mouse pointer is outside the target window, the resulting X,Y from `ClientToScreen()` is screen coordinate.
* When the mouse pointer is inside the target window, the resulting X,Y from `ClientToScreen()` is in target-window's "back-off 100% scaling" perspective.

As depicted in the figures below, there are two mouse positions that will can cause `ClientToScreen()` to report X=580 and Y=150, one outside target window, another inside target window.

![Win7-ghost-window-out](Graphics/Screenshots/Win7-ghost-window-out.png)

![Win7-not-ghost-window-in](Graphics/Screenshots/Win7-not-ghost-window-in.png)


So the outside one causes ghost window symptom.

In Win10.1909, for DPI-unaware wcFinder caller, `ClientToScreen()` always reports value in "back-off 100% scaling" perspective, thus gone the bug.

Loading