Skip to content

Commit fa7dd56

Browse files
committed
Improve method for spy window selection
1 parent d670175 commit fa7dd56

File tree

6 files changed

+150
-107
lines changed

6 files changed

+150
-107
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,13 @@ window will minimize the standard way instead of to the tray.
213213
### Spy feature
214214

215215
If you don't know the executable, class, or title of a window, or just would like help filling those values, you can use
216-
the Spy feature. To use it, first open the [Settings](#settings) window, then press the "Spy" button. This will close
217-
the Settings window, then show you a message explaining that Spy mode is active, and that you can click on any window to
218-
select it. Press the "OK" button of that message, and then click on the window you want to obtain the executable, class,
219-
and title of. Once you have done that, the Settings window should re-appear, and those values should be filled in. You
220-
can modify them if you like, or leave them alone, and then press the Add button to create a new Auto-tray item for that
221-
window.
216+
the Spy feature. To use it, first open the [Settings](#settings) window, find the crosshair icon next to the text that
217+
says "Spy (drag this):", and use your mouse to drag that icon onto the window that you want to obtain the executable,
218+
class, and title of. As you drag the crosshair around, the settings window should be updated with information about each
219+
window you move it over, and when you release the mouse button then the crosshair icon will return to its original
220+
position, and the values in the settings should stay filled in with whatever window you last dragged on top of. You can
221+
modify the values if you like, or leave them alone, and then press the Add button to create a new Auto-tray item for
222+
that window.
222223

223224
If the Spy feature doesn't work for you, alternatively you can use another tool like
224225
[Window Spy](https://amourspirit.github.io/AutoHotkey-Snippit/WindowSpy.html) or

TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
- Get windows error reporting (WER) working
44
- Add tray icon promotion (avoid moving to notification area overflow menu)
5-
- Improve method for spy window selection
65
- Investigate VirusTotal results for installer
76
- Add auto-tray option: when created, when minimized, both
87
- Add option to make tray icons persistent even when window is restored

src/Finestray.rc

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ BEGIN
4646
IDS_COLUMN_EXECUTABLE "Executable"
4747
IDS_COLUMN_WINDOW_CLASS "Class"
4848
IDS_COLUMN_WINDOW_TITLE "Title"
49-
IDS_SPY_MODE_TITLE "Spy Mode"
50-
IDS_SPY_MODE_TEXT "Spy mode activated, click on any window to retrieve its information."
5149
IDS_ERROR_INIT_COM "Failed to initialize COM"
5250
IDS_ERROR_INIT_COMMON_CONTROLS "Failed to initialize common controls"
5351
IDS_ERROR_REGISTER_WINDOW_CLASS "Error creating window class"
@@ -64,7 +62,7 @@ BEGIN
6462
IDS_ERROR_SAVE_SETTINGS "Failed to save settings"
6563
END
6664

67-
IDD_DIALOG_SETTINGS DIALOGEX 0, 0, 420, 315
65+
IDD_DIALOG_SETTINGS DIALOGEX 0, 0, 420, 317
6866
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
6967
CAPTION "Finestray Settings"
7068
FONT 8, "MS Shell Dlg", 400, 0, 0x1
@@ -99,7 +97,7 @@ BEGIN
9997
LTEXT "Poll Interval", IDC_STATIC, 10, 97, 67, 8, SS_RIGHT | WS_GROUP
10098
EDITTEXT IDC_POLL_INTERVAL, 81, 95, 120, 14, ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP
10199

102-
GROUPBOX "Auto-tray windows", IDC_STATIC, 5, 114, 410, 176
100+
GROUPBOX "Auto-tray windows", IDC_STATIC, 5, 114, 410, 177
103101
CONTROL "", IDC_AUTO_TRAY_LIST, WC_LISTVIEWA, LVS_NOSORTHEADER | LVS_REPORT | LVS_SINGLESEL | WS_TABSTOP, 9, 126, 400, 89
104102
LTEXT "Executable", IDC_STATIC, 10, 221, 67, 8, SS_RIGHT | WS_GROUP
105103
EDITTEXT IDC_AUTO_TRAY_EDIT_EXECUTABLE, 81, 219, 329, 14, ES_AUTOHSCROLL | WS_TABSTOP
@@ -110,14 +108,15 @@ BEGIN
110108
PUSHBUTTON "Update", IDC_AUTO_TRAY_ITEM_UPDATE, 80, 272, 50, 12
111109
PUSHBUTTON "Add", IDC_AUTO_TRAY_ITEM_ADD, 133, 272, 50, 12
112110
PUSHBUTTON "Delete", IDC_AUTO_TRAY_ITEM_DELETE, 186, 272, 50, 12
113-
PUSHBUTTON "Spy", IDC_AUTO_TRAY_ITEM_SPY, 360, 272, 50, 12
114-
115-
PUSHBUTTON "Help", IDC_HELP_PAGE, 10, 296, 50, 14
116-
PUSHBUTTON "About", IDC_ABOUT, 64, 296, 50, 14
117-
PUSHBUTTON "Reset", IDC_RESET, 118, 296, 50, 14
118-
PUSHBUTTON "Exit", IDC_EXIT, 172, 296, 50, 14
119-
PUSHBUTTON "Cancel", IDCANCEL, 305, 296, 50, 14
120-
DEFPUSHBUTTON "OK", IDOK, 359, 296, 50, 14
111+
LTEXT "Spy (drag this):", IDC_STATIC, 338, 274, 50, 12
112+
ICON "",IDC_AUTO_TRAY_ITEM_SPY, 390, 269, 20, 20, SS_NOTIFY
113+
114+
PUSHBUTTON "Help", IDC_HELP_PAGE, 10, 298, 50, 14
115+
PUSHBUTTON "About", IDC_ABOUT, 64, 298, 50, 14
116+
PUSHBUTTON "Reset", IDC_RESET, 118, 298, 50, 14
117+
PUSHBUTTON "Exit", IDC_EXIT, 172, 298, 50, 14
118+
PUSHBUTTON "Cancel", IDCANCEL, 305, 298, 50, 14
119+
DEFPUSHBUTTON "OK", IDOK, 359, 298, 50, 14
121120
END
122121

123122
VS_VERSION_INFO VERSIONINFO

src/Resource.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
#define IDS_COLUMN_EXECUTABLE 222
3131
#define IDS_COLUMN_WINDOW_CLASS 223
3232
#define IDS_COLUMN_WINDOW_TITLE 224
33-
#define IDS_SPY_MODE_TITLE 225
34-
#define IDS_SPY_MODE_TEXT 226
3533

3634
// error strings
3735
#define IDS_ERROR_INIT_COM 301

src/SettingsDialog.cpp

Lines changed: 132 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ int autoTrayListViewCompare(LPARAM, LPARAM, LPARAM);
5757
void autoTrayListViewItemAdd(HWND dialogHwnd);
5858
void autoTrayListViewItemUpdate(HWND dialogHwnd, int item);
5959
void autoTrayListViewItemDelete(HWND dialogHwnd, int item);
60-
void autoTrayListViewItemSpy(HWND dialogHwnd);
6160
void autoTrayListViewItemEdit(HWND dialogHwnd, int item);
6261
void autoTrayListViewUpdateButtons(HWND dialogHwnd);
6362
void autoTrayListViewUpdateSelected(HWND dialogHwnd);
64-
void spySelectWindowAtPoint(const POINT & point);
65-
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
63+
void spyBegin(HWND dialogHwnd);
64+
void spyEnd(HWND dialogHwnd);
65+
void spyEnableIcon(HWND dialogHwnd);
66+
void spyDisableIcon(HWND dialogHwnd);
67+
void spyUpdate(HWND dialogHwnd);
6668
std::string getDialogItemText(HWND dialogHwnd, int id);
6769
std::string getListViewItemText(HWND listViewHwnd, int item, int subItem);
6870

@@ -75,8 +77,6 @@ bool autoTrayListViewSortAscending_;
7577
int autoTrayListViewSortColumn_;
7678
#endif
7779
bool spyMode_;
78-
HWND spyModeHwnd_;
79-
HHOOK mouseHhook_;
8080

8181
} // anonymous namespace
8282

@@ -110,12 +110,29 @@ INT_PTR settingsDialogFunc(HWND dialogHwnd, UINT message, WPARAM wParam, LPARAM
110110
// DEBUG_PRINTF("wnd %#x, message %#x, wparam %#x, lparam %#x\n", dialogHwnd, message, wParam, lParam);
111111

112112
if (spyMode_) {
113-
if (message == WM_LBUTTONDOWN) {
114-
POINT point;
115-
if (!GetCursorPos(&point)) {
116-
WARNING_PRINTF("GetCursorPos failed: %s\n", StringUtility::lastErrorString().c_str());
117-
} else {
118-
spySelectWindowAtPoint(point);
113+
switch (message) {
114+
case WM_CAPTURECHANGED:
115+
case WM_LBUTTONUP: {
116+
spyUpdate(dialogHwnd);
117+
spyEnd(dialogHwnd);
118+
break;
119+
}
120+
121+
case WM_MOUSEMOVE: {
122+
spyUpdate(dialogHwnd);
123+
break;
124+
}
125+
126+
case WM_SETCURSOR: {
127+
HCURSOR hCursor = LoadCursor(NULL, IDC_CROSS);
128+
if (!hCursor) {
129+
WARNING_PRINTF("LoadCursor failed: %s\n", StringUtility::lastErrorString().c_str());
130+
} else {
131+
if (!SetCursor(hCursor)) {
132+
WARNING_PRINTF("SetCursor failed: %s\n", StringUtility::lastErrorString().c_str());
133+
}
134+
}
135+
break;
119136
}
120137
}
121138

@@ -193,6 +210,8 @@ INT_PTR settingsDialogFunc(HWND dialogHwnd, UINT message, WPARAM wParam, LPARAM
193210
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
194211
}
195212

213+
spyEnableIcon(dialogHwnd);
214+
196215
autoTrayListViewInit(dialogHwnd);
197216

198217
break;
@@ -258,7 +277,9 @@ INT_PTR settingsDialogFunc(HWND dialogHwnd, UINT message, WPARAM wParam, LPARAM
258277
break;
259278
}
260279
case IDC_AUTO_TRAY_ITEM_SPY: {
261-
autoTrayListViewItemSpy(dialogHwnd);
280+
if (HIWORD(wParam) == STN_CLICKED) {
281+
spyBegin(dialogHwnd);
282+
}
262283
break;
263284
}
264285

@@ -670,31 +691,6 @@ void autoTrayListViewItemDelete(HWND dialogHwnd, int item)
670691
autoTrayListViewUpdateSelected(dialogHwnd);
671692
}
672693

673-
void autoTrayListViewItemSpy(HWND dialogHwnd)
674-
{
675-
DEBUG_PRINTF("Spying auto tray\n");
676-
677-
if (!ShowWindow(dialogHwnd, SW_HIDE)) {
678-
WARNING_PRINTF("ShowWindow failed: %s\n", StringUtility::lastErrorString().c_str());
679-
}
680-
681-
MessageBoxA(
682-
dialogHwnd,
683-
getResourceString(IDS_SPY_MODE_TEXT).c_str(),
684-
getResourceString(IDS_SPY_MODE_TITLE).c_str(),
685-
MB_OK);
686-
687-
mouseHhook_ = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, nullptr, 0);
688-
if (!mouseHhook_) {
689-
WARNING_PRINTF("Failed to install mouse hook!\n");
690-
} else {
691-
DEBUG_PRINTF("Mouse hook installed.\n");
692-
}
693-
694-
spyModeHwnd_ = dialogHwnd;
695-
spyMode_ = true;
696-
}
697-
698694
void autoTrayListViewItemEdit(HWND dialogHwnd, int item)
699695
{
700696
DEBUG_PRINTF("Editing auto tray item %d\n", item);
@@ -822,76 +818,126 @@ void setListViewSortIcon(HWND listView, int col, int sortOrder)
822818
}
823819
#endif
824820

825-
void spySelectWindowAtPoint(const POINT & point)
821+
void spyBegin(HWND dialogHwnd)
826822
{
827-
DEBUG_PRINTF("Mouse clicked at: %d, %d\n", point.x, point.y);
823+
DEBUG_PRINTF("Spy mode: beginning\n");
828824

829-
HWND hwnd = WindowFromPoint(point);
830-
if (!hwnd) {
831-
DEBUG_PRINTF("No window found\n");
832-
} else {
833-
DEBUG_PRINTF("Spy mode: hwnd %#x\n", hwnd);
825+
spyMode_ = true;
826+
827+
spyDisableIcon(dialogHwnd);
828+
829+
SetCapture(dialogHwnd);
830+
}
831+
832+
void spyEnd(HWND dialogHwnd)
833+
{
834+
DEBUG_PRINTF("Spy mode: ended\n");
835+
if (!ReleaseCapture()) {
836+
WARNING_PRINTF("ReleaseCapture failed: %s\n", StringUtility::lastErrorString().c_str());
837+
}
838+
839+
spyEnableIcon(dialogHwnd);
834840

835-
HWND rootHwnd = GetAncestor(hwnd, GA_ROOT);
836-
if (!rootHwnd) {
837-
WARNING_PRINTF("Failed to get root hwnd, falling back to original\n");
838-
rootHwnd = hwnd;
841+
spyMode_ = false;
842+
}
843+
844+
void spyEnableIcon(HWND dialogHwnd)
845+
{
846+
HCURSOR hCrossCursor = LoadCursor(NULL, IDC_CROSS);
847+
if (!hCrossCursor) {
848+
WARNING_PRINTF("LoadCursor failed: %s\n", StringUtility::lastErrorString().c_str());
849+
} else {
850+
if (!SendDlgItemMessage(dialogHwnd, IDC_AUTO_TRAY_ITEM_SPY, STM_SETICON, (WPARAM)hCrossCursor, 0)) {
851+
WARNING_PRINTF("SendDlgItemMessage failed: %s\n", StringUtility::lastErrorString().c_str());
839852
}
853+
}
854+
}
840855

841-
CHAR executableFullPath[MAX_PATH] = {};
842-
DWORD processID;
843-
if (!GetWindowThreadProcessId(rootHwnd, &processID)) {
844-
WARNING_PRINTF("GetWindowThreadProcessId() failed: %s\n", StringUtility::lastErrorString().c_str());
845-
} else {
846-
HandleWrapper process(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID));
847-
if (!process) {
848-
WARNING_PRINTF("OpenProcess() failed: %s\n", StringUtility::lastErrorString().c_str());
849-
} else {
850-
if (!GetModuleFileNameExA((HMODULE)(HANDLE)process, nullptr, executableFullPath, MAX_PATH)) {
851-
WARNING_PRINTF("GetModuleFileNameA() failed: %s\n", StringUtility::lastErrorString().c_str());
852-
}
853-
}
856+
void spyDisableIcon(HWND dialogHwnd)
857+
{
858+
if (!SendDlgItemMessage(dialogHwnd, IDC_AUTO_TRAY_ITEM_SPY, STM_SETICON, 0, 0)) {
859+
WARNING_PRINTF("SendDlgItemMessage failed: %s\n", StringUtility::lastErrorString().c_str());
860+
}
861+
862+
HCURSOR hCursor = LoadCursor(NULL, IDC_CROSS);
863+
if (!hCursor) {
864+
WARNING_PRINTF("LoadCursor failed: %s\n", StringUtility::lastErrorString().c_str());
865+
} else {
866+
if (!SetCursor(hCursor)) {
867+
WARNING_PRINTF("SetCursor failed: %s\n", StringUtility::lastErrorString().c_str());
854868
}
855-
DEBUG_PRINTF("Executable full path: '%s'\n", executableFullPath);
869+
}
870+
}
856871

857-
std::string className = getWindowClassName(rootHwnd);
858-
DEBUG_PRINTF("Class name: '%s'\n", className.c_str());
872+
void spyUpdate(HWND dialogHwnd)
873+
{
874+
POINT point;
875+
if (!GetCursorPos(&point)) {
876+
WARNING_PRINTF("GetCursorPos failed: %s\n", StringUtility::lastErrorString().c_str());
877+
return;
878+
}
879+
880+
DEBUG_PRINTF("Spy mode: selecting window at: %d, %d\n", point.x, point.y);
881+
882+
HWND hwnd = WindowFromPoint(point);
883+
if (!hwnd) {
884+
DEBUG_PRINTF("No window found\n");
885+
return;
886+
}
859887

860-
std::string title = getWindowText(rootHwnd);
861-
DEBUG_PRINTF("Title: '%s'\n", title.c_str());
888+
HWND rootHwnd = GetAncestor(hwnd, GA_ROOT);
889+
if (!rootHwnd) {
890+
WARNING_PRINTF("Failed to get root hwnd, falling back to original\n");
891+
rootHwnd = hwnd;
892+
}
862893

863-
if (!SetDlgItemTextA(spyModeHwnd_, IDC_AUTO_TRAY_EDIT_EXECUTABLE, executableFullPath)) {
894+
if (rootHwnd == dialogHwnd) {
895+
DEBUG_PRINTF("Spy mode: own window, clearing\n");
896+
if (!SetDlgItemTextA(dialogHwnd, IDC_AUTO_TRAY_EDIT_EXECUTABLE, nullptr)) {
864897
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
865898
}
866-
if (!SetDlgItemTextA(spyModeHwnd_, IDC_AUTO_TRAY_EDIT_WINDOWCLASS, className.c_str())) {
899+
if (!SetDlgItemTextA(dialogHwnd, IDC_AUTO_TRAY_EDIT_WINDOWCLASS, nullptr)) {
867900
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
868901
}
869-
if (!SetDlgItemTextA(spyModeHwnd_, IDC_AUTO_TRAY_EDIT_WINDOWTITLE, title.c_str())) {
902+
if (!SetDlgItemTextA(dialogHwnd, IDC_AUTO_TRAY_EDIT_WINDOWTITLE, nullptr)) {
870903
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
871904
}
905+
return;
906+
}
872907

873-
if (!ShowWindow(spyModeHwnd_, SW_SHOW)) {
874-
WARNING_PRINTF("ShowWindow failed: %s\n", StringUtility::lastErrorString().c_str());
875-
}
876-
if (!SetForegroundWindow(spyModeHwnd_)) {
877-
WARNING_PRINTF("SetForegroundWindow failed: %s\n", StringUtility::lastErrorString().c_str());
878-
}
908+
DEBUG_PRINTF("Spy mode: root hwnd %#x\n", rootHwnd);
879909

880-
spyModeHwnd_ = nullptr;
881-
spyMode_ = false;
910+
CHAR executableFullPath[MAX_PATH] = {};
911+
DWORD processID;
912+
if (!GetWindowThreadProcessId(rootHwnd, &processID)) {
913+
WARNING_PRINTF("GetWindowThreadProcessId() failed: %s\n", StringUtility::lastErrorString().c_str());
914+
} else {
915+
HandleWrapper process(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID));
916+
if (!process) {
917+
WARNING_PRINTF("OpenProcess() failed: %s\n", StringUtility::lastErrorString().c_str());
918+
} else {
919+
if (!GetModuleFileNameExA((HMODULE)(HANDLE)process, nullptr, executableFullPath, MAX_PATH)) {
920+
WARNING_PRINTF("GetModuleFileNameA() failed: %s\n", StringUtility::lastErrorString().c_str());
921+
}
922+
}
882923
}
883-
}
924+
DEBUG_PRINTF("Executable full path: '%s'\n", executableFullPath);
884925

885-
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
886-
{
887-
if ((nCode == HC_ACTION) && (wParam == WM_LBUTTONDOWN)) {
888-
UnhookWindowsHookEx(mouseHhook_);
926+
std::string className = getWindowClassName(rootHwnd);
927+
DEBUG_PRINTF("Class name: '%s'\n", className.c_str());
889928

890-
LPMSLLHOOKSTRUCT mouseInfo = (LPMSLLHOOKSTRUCT)lParam;
891-
spySelectWindowAtPoint(mouseInfo->pt);
892-
}
929+
std::string title = getWindowText(rootHwnd);
930+
DEBUG_PRINTF("Title: '%s'\n", title.c_str());
893931

894-
return CallNextHookEx(mouseHhook_, nCode, wParam, lParam);
932+
if (!SetDlgItemTextA(dialogHwnd, IDC_AUTO_TRAY_EDIT_EXECUTABLE, executableFullPath)) {
933+
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
934+
}
935+
if (!SetDlgItemTextA(dialogHwnd, IDC_AUTO_TRAY_EDIT_WINDOWCLASS, className.c_str())) {
936+
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
937+
}
938+
if (!SetDlgItemTextA(dialogHwnd, IDC_AUTO_TRAY_EDIT_WINDOWTITLE, title.c_str())) {
939+
WARNING_PRINTF("SetDlgItemTextA failed: %s\n", StringUtility::lastErrorString().c_str());
940+
}
895941
}
896942

897943
std::string getDialogItemText(HWND dialogHwnd, int id)

src/images/settings-window.png

179 Bytes
Loading

0 commit comments

Comments
 (0)