├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── icon
└── traymond.ico
├── images
├── logo-sm.png
├── logo.png
├── options-hotkey.png
├── options.png
├── popup-menu.png
└── rules.png
├── locale
└── en_US
│ ├── Traymond.rc
│ ├── en_US.vcxproj
│ └── en_US.vcxproj.filters
├── src
├── Traymond.rc
├── i18n.cpp
├── i18n.h
├── icons.cpp
├── icons.h
├── logging.h
├── options.cpp
├── options.h
├── resource.h
├── rules.cpp
├── rules.h
├── traymond.cpp
├── traymond.h
├── winevent.cpp
└── winevent.h
├── traymond.sln
├── traymond.vcxproj
└── traymond.vcxproj.filters
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build results from nmake
2 | /*.obj
3 | /*.res
4 | /*.exe
5 |
6 | ## Ignore Visual Studio temporary files, build results, and
7 | ## files generated by popular Visual Studio add-ons.
8 | ##
9 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.userosscache
15 | *.sln.docstates
16 |
17 | # User-specific files (MonoDevelop/Xamarin Studio)
18 | *.userprefs
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | bld/
28 | [Bb]in/
29 | [Oo]bj/
30 | [Ll]og/
31 |
32 | # Visual Studio 2015/2017 cache/options directory
33 | .vs/
34 | # Uncomment if you have tasks that create the project's static files in wwwroot
35 | #wwwroot/
36 |
37 | # Visual Studio 2017 auto generated files
38 | Generated\ Files/
39 |
40 | # MSTest test Results
41 | [Tt]est[Rr]esult*/
42 | [Bb]uild[Ll]og.*
43 |
44 | # NUNIT
45 | *.VisualState.xml
46 | TestResult.xml
47 |
48 | # Build Results of an ATL Project
49 | [Dd]ebugPS/
50 | [Rr]eleasePS/
51 | dlldata.c
52 |
53 | # Benchmark Results
54 | BenchmarkDotNet.Artifacts/
55 |
56 | # .NET Core
57 | project.lock.json
58 | project.fragment.lock.json
59 | artifacts/
60 |
61 | # StyleCop
62 | StyleCopReport.xml
63 |
64 | # Files built by Visual Studio
65 | *_i.c
66 | *_p.c
67 | *_i.h
68 | *.ilk
69 | *.meta
70 | *.obj
71 | *.iobj
72 | *.pch
73 | *.pdb
74 | *.ipdb
75 | *.pgc
76 | *.pgd
77 | *.rsp
78 | *.sbr
79 | *.tlb
80 | *.tli
81 | *.tlh
82 | *.tmp
83 | *.tmp_proj
84 | *.log
85 | *.vspscc
86 | *.vssscc
87 | .builds
88 | *.pidb
89 | *.svclog
90 | *.scc
91 |
92 | # Chutzpah Test files
93 | _Chutzpah*
94 |
95 | # Visual C++ cache files
96 | ipch/
97 | *.aps
98 | *.ncb
99 | *.opendb
100 | *.opensdf
101 | *.sdf
102 | *.cachefile
103 | *.VC.db
104 | *.VC.VC.opendb
105 |
106 | # Visual Studio profiler
107 | *.psess
108 | *.vsp
109 | *.vspx
110 | *.sap
111 |
112 | # Visual Studio Trace Files
113 | *.e2e
114 |
115 | # TFS 2012 Local Workspace
116 | $tf/
117 |
118 | # Guidance Automation Toolkit
119 | *.gpState
120 |
121 | # ReSharper is a .NET coding add-in
122 | _ReSharper*/
123 | *.[Rr]e[Ss]harper
124 | *.DotSettings.user
125 |
126 | # JustCode is a .NET coding add-in
127 | .JustCode
128 |
129 | # TeamCity is a build add-in
130 | _TeamCity*
131 |
132 | # DotCover is a Code Coverage Tool
133 | *.dotCover
134 |
135 | # AxoCover is a Code Coverage Tool
136 | .axoCover/*
137 | !.axoCover/settings.json
138 |
139 | # Visual Studio code coverage results
140 | *.coverage
141 | *.coveragexml
142 |
143 | # NCrunch
144 | _NCrunch_*
145 | .*crunch*.local.xml
146 | nCrunchTemp_*
147 |
148 | # MightyMoose
149 | *.mm.*
150 | AutoTest.Net/
151 |
152 | # Web workbench (sass)
153 | .sass-cache/
154 |
155 | # Installshield output folder
156 | [Ee]xpress/
157 |
158 | # DocProject is a documentation generator add-in
159 | DocProject/buildhelp/
160 | DocProject/Help/*.HxT
161 | DocProject/Help/*.HxC
162 | DocProject/Help/*.hhc
163 | DocProject/Help/*.hhk
164 | DocProject/Help/*.hhp
165 | DocProject/Help/Html2
166 | DocProject/Help/html
167 |
168 | # Click-Once directory
169 | publish/
170 |
171 | # Publish Web Output
172 | *.[Pp]ublish.xml
173 | *.azurePubxml
174 | # Note: Comment the next line if you want to checkin your web deploy settings,
175 | # but database connection strings (with potential passwords) will be unencrypted
176 | *.pubxml
177 | *.publishproj
178 |
179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
180 | # checkin your Azure Web App publish settings, but sensitive information contained
181 | # in these scripts will be unencrypted
182 | PublishScripts/
183 |
184 | # NuGet Packages
185 | *.nupkg
186 | # The packages folder can be ignored because of Package Restore
187 | **/[Pp]ackages/*
188 | # except build/, which is used as an MSBuild target.
189 | !**/[Pp]ackages/build/
190 | # Uncomment if necessary however generally it will be regenerated when needed
191 | #!**/[Pp]ackages/repositories.config
192 | # NuGet v3's project.json files produces more ignorable files
193 | *.nuget.props
194 | *.nuget.targets
195 |
196 | # Microsoft Azure Build Output
197 | csx/
198 | *.build.csdef
199 |
200 | # Microsoft Azure Emulator
201 | ecf/
202 | rcf/
203 |
204 | # Windows Store app package directories and files
205 | AppPackages/
206 | BundleArtifacts/
207 | Package.StoreAssociation.xml
208 | _pkginfo.txt
209 | *.appx
210 |
211 | # Visual Studio cache files
212 | # files ending in .cache can be ignored
213 | *.[Cc]ache
214 | # but keep track of directories ending in .cache
215 | !*.[Cc]ache/
216 |
217 | # Others
218 | ClientBin/
219 | ~$*
220 | *~
221 | *.dbmdl
222 | *.dbproj.schemaview
223 | *.jfm
224 | *.pfx
225 | *.publishsettings
226 | orleans.codegen.cs
227 |
228 | # Including strong name files can present a security risk
229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
230 | #*.snk
231 |
232 | # Since there are multiple workflows, uncomment next line to ignore bower_components
233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
234 | #bower_components/
235 |
236 | # RIA/Silverlight projects
237 | Generated_Code/
238 |
239 | # Backup & report files from converting an old project file
240 | # to a newer Visual Studio version. Backup files are not needed,
241 | # because we have git ;-)
242 | _UpgradeReport_Files/
243 | Backup*/
244 | UpgradeLog*.XML
245 | UpgradeLog*.htm
246 | ServiceFabricBackup/
247 | *.rptproj.bak
248 |
249 | # SQL Server files
250 | *.mdf
251 | *.ldf
252 | *.ndf
253 |
254 | # Business Intelligence projects
255 | *.rdl.data
256 | *.bim.layout
257 | *.bim_*.settings
258 | *.rptproj.rsuser
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush
299 | .cr/
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Fork and make a pull request. Explain why you want it to be merged.
4 |
5 | I appreciate every bit of help since I don't have much time for this project anymore.
6 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 fcFn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CPPFLAGS=/nologo /EHsc /O2 /GL /GS /Oi /MD /D "_UNICODE" /D "UNICODE" /Zc:inline
2 | RFLAGS=/nologo /n /r
3 |
4 | {src\}.cpp.obj:
5 | $(CPP) $(CPPFLAGS) /c $<
6 | {src\}.rc.res:
7 | $(RC) $(RFLAGS) /fo$(@F) $<
8 |
9 | Traymond.exe: options.obj rules.obj winevent.obj icons.obj traymond.obj Traymond.res
10 | $(CPP) $(CPPFLAGS) /Fe$(@F) $** user32.lib shell32.lib comctl32.lib advapi32.lib gdi32.lib /link /MACHINE:X86 /MANIFESTDEPENDENCY:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'"
11 |
12 | clean:
13 | del *.obj *.res Traymond.exe.manifest Traymond.exe
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |  Traymond(增强版)
2 | =======
3 |
4 | Traymond(增强版)是一款能将任意窗口最小化到系统托盘的 Windows 桌面工具。此项目受 [Traymond](https://github.com/fcFn/traymond) 启发,实现以下功能:
5 |
6 | - 中文界面
7 | - 开机自动运行
8 | - 可自定义热键
9 | - 最小化窗口到系统托盘图标或右键菜单
10 | - 还原最后一个最小化窗口
11 | - 唤出最小化窗口列表
12 | - 通过自定义规则自动最小化窗口
13 |
14 | ## 安装
15 |
16 | 1. 从 https://github.com/tabris17/traymond/releases/latest 下载可执行文件直接运行;
17 |
18 | 2. 使用 Scoop
19 |
20 | ```cmd
21 | scoop install https://github.com/tabris17/traymond/releases/latest/download/traymond.json
22 | ```
23 |
24 | ## 用法
25 |
26 | 程序运行后会常驻系统托盘。按下默认热键 Win + Shift + Z 最小化当前窗口到系统托盘。可以在“选项”中设置最小化窗口收纳至托盘图标或者右键菜单。
27 |
28 | 
29 |
30 | 鼠标双击托盘图标  或者在右键菜单中选择“选项”,打开“Traymond 选项” 对话框。
31 |
32 | 
33 |
34 | ### 自定义热键
35 |
36 | 在“自定义热键”列表内选中需要设置热键的行为,在下方的热键控件中按下自定义热键组合。由于热键控件无法响应按下 Win 键,如果要使用 Win 作为修饰键,请勾选“使用 Win 键” 选择框。
37 |
38 | 
39 |
40 | ### 自动最小化窗口
41 |
42 | 在“Traymond 选项”对话框中勾选“运行期间自动最小化指定窗口”选择框,以启用自动最小化窗口功能。点击“自定义规则”按钮设置规则。
43 |
44 | 
45 |
46 | 具体设置方法请参考项目[维基](https://github.com/tabris17/traymond/wiki)。
--------------------------------------------------------------------------------
/icon/traymond.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/icon/traymond.ico
--------------------------------------------------------------------------------
/images/logo-sm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/images/logo-sm.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/images/logo.png
--------------------------------------------------------------------------------
/images/options-hotkey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/images/options-hotkey.png
--------------------------------------------------------------------------------
/images/options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/images/options.png
--------------------------------------------------------------------------------
/images/popup-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/images/popup-menu.png
--------------------------------------------------------------------------------
/images/rules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tabris17/traymond/9623c4eca602a05d6d593e52afaac9d44b9ef85e/images/rules.png
--------------------------------------------------------------------------------
/locale/en_US/Traymond.rc:
--------------------------------------------------------------------------------
1 | #pragma code_page(65001)
2 |
3 | #include "resource.h"
4 |
5 |
6 | /////////////////////////////////////////////////////////////////////////////
7 | //
8 | // Menu
9 | //
10 |
11 | IDM_POPUP MENU
12 | BEGIN
13 | POPUP ""
14 | BEGIN
15 | MENUITEM "Restore all windows", IDM_RESTORE_ALL_WINDOWS
16 | MENUITEM "Restore the last window", IDM_RESTORE_LAST_WINDOW
17 | MENUITEM "Options", IDM_OPTIONS
18 | MENUITEM SEPARATOR
19 | MENUITEM "Exit", IDM_EXIT
20 | END
21 | END
22 |
23 |
24 | /////////////////////////////////////////////////////////////////////////////
25 | //
26 | // Dialog
27 | //
28 |
29 | IDD_OPTIONS DIALOGEX 0, 0, 200, 204
30 | STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
31 | EXSTYLE WS_EX_STATICEDGE
32 | CAPTION "Traymond Options"
33 | FONT 9, "MS Shell Dlg", 400, 0, 0x1
34 | BEGIN
35 | DEFPUSHBUTTON "OK",IDOK,49,184,50,14
36 | PUSHBUTTON "Cancel",IDCANCEL,103,184,50,14
37 | GROUPBOX "Custom hotkeys",IDC_STATIC,6,6,188,105
38 | CONTROL "",IDC_HOTKEY_LIST,"SysListView32",LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_REPORT | WS_BORDER | WS_TABSTOP,12,18,176,66
39 | CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP | WS_DISABLED,12,90,84,14
40 | CONTROL "Use Win key",IDC_CHECK_USE_WIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED,102,92,58,11
41 | LTEXT "Minimize the window to ",IDC_STATIC,6,124,85,10
42 | COMBOBOX IDC_COMBO_HIDE_TYPE,90,122,66,120,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_HASSTRINGS | WS_TABSTOP
43 | CONTROL "Autorun at startup",IDC_CHECK_AUTORUN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,143,72,11
44 | CONTROL "Auto-minimize the specified window",IDC_CHECK_AUTO_HIDING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,159,125,11
45 | PUSHBUTTON "Custom rules",IDC_BUTTON_RULES,134,157,60,14,WS_DISABLED
46 | END
47 |
48 | IDD_RULES DIALOGEX 0, 0, 400, 306
49 | STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | DS_SETFOREGROUND | DS_SHELLFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU
50 | CAPTION "Traymond Custom Rules"
51 | FONT 9, "MS Shell Dlg", 400, 0, 0x1
52 | BEGIN
53 | DEFPUSHBUTTON "Close", IDCANCEL, 345, 286, 50, 14, BS_FLAT
54 | PUSHBUTTON "Cancel", IDABORT, 290, 286, 50, 14, BS_FLAT | WS_DISABLED
55 | PUSHBUTTON "Help", IDHELP, 236, 286, 50, 14, BS_FLAT
56 | LISTBOX IDC_LIST_RULES, 5, 5, 135, 276, LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
57 | PUSHBUTTON "New", IDC_NEW, 5, 286, 42, 14, BS_FLAT
58 | PUSHBUTTON "Save", IDC_SAVE, 51, 286, 42, 14, BS_FLAT | WS_DISABLED
59 | PUSHBUTTON "Remove", IDC_REMOVE, 97, 286, 42, 14, BS_FLAT | WS_DISABLED
60 | LTEXT "Rule name:", IDC_STATIC, 145, 8, 264, 10
61 | EDITTEXT IDC_EDIT_NAME, 145, 20, 250, 14, ES_AUTOHSCROLL | WS_DISABLED
62 | LTEXT "Create the rule from a minimized window:", IDC_STATIC, 145, 40, 280, 10
63 | COMBOBOX IDC_COMBO_WINDOWS, 145, 52, 250, 200, CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_HASSTRINGS | WS_TABSTOP | WS_DISABLED
64 | GROUPBOX "Window title", IDC_STATIC, 145, 70, 250, 45
65 | EDITTEXT IDC_EDIT_TEXT, 153, 84, 235, 14, ES_AUTOHSCROLL | WS_DISABLED
66 | CONTROL "Regex", IDC_CHECK_REGEX_TEXT, "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED, 153, 99, 71, 12
67 | GROUPBOX "Window class", IDC_STATIC, 145, 119, 250, 45
68 | EDITTEXT IDC_EDIT_CLASS, 153, 133, 235, 14, ES_AUTOHSCROLL | WS_DISABLED
69 | CONTROL "Regex", IDC_CHECK_REGEX_CLASS, "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED, 153, 148, 71, 12
70 | GROUPBOX "Image path", IDC_STATIC, 145, 168, 250, 45
71 | EDITTEXT IDC_EDIT_PATH, 153, 182, 235, 14, ES_AUTOHSCROLL | WS_DISABLED
72 | CONTROL "Regex", IDC_CHECK_REGEX_PATH, "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED, 153, 197, 71, 12
73 | GROUPBOX "Rule in effect", IDC_STATIC, 145, 217, 250, 30
74 | CONTROL "Window first shown", IDC_RADIO_ON_FIRST_SHOW, "Button", BS_AUTORADIOBUTTON | WS_GROUP | WS_DISABLED, 153, 229, 80, 13
75 | CONTROL "Window minimized", IDC_RADIO_ON_MINIMIZE, "Button", BS_AUTORADIOBUTTON | WS_DISABLED, 235, 229, 75, 13
76 | CONTROL "Both", IDC_RADIO_ON_BOTH, "Button", BS_AUTORADIOBUTTON | WS_DISABLED, 315, 229, 33, 13
77 | GROUPBOX "Window minimize notification", IDC_STATIC, 145, 251, 250, 30
78 | CONTROL "Never", IDC_RADIO_NEVER_NOTIFY, "Button", BS_AUTORADIOBUTTON | WS_GROUP | WS_DISABLED, 153, 263, 28, 13
79 | CONTROL "Always", IDC_RADIO_ALWAYS_NOTIFY, "Button", BS_AUTORADIOBUTTON | WS_DISABLED, 195, 263, 35, 13
80 | CONTROL "Only first time", IDC_RADIO_NOTIFY_FIRST_TIME, "Button", BS_AUTORADIOBUTTON | WS_DISABLED, 240, 263, 100, 13
81 | END
82 |
83 | IDD_ICONS DIALOGEX 0, 0, 120, 100
84 | STYLE DS_SETFONT | DS_CENTERMOUSE | DS_SETFOREGROUND | DS_SHELLFONT | WS_POPUP
85 | FONT 12, "MS Shell Dlg", 400, 0, 0x1
86 | CLASS POPUP_CLASS
87 | BEGIN
88 | CONTROL "",IDC_ICON_LIST,"SysListView32",LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_REPORT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,0,0,120,100
89 | END
90 |
91 |
92 | /////////////////////////////////////////////////////////////////////////////
93 | //
94 | // String Table
95 | //
96 |
97 | STRINGTABLE
98 | BEGIN
99 | IDS_HOTKEY_ERROR "Unable to register the global hotkey %s.\nPlease select another one."
100 | IDS_MUTEX_ERROR "Fatal error: failed to create the mutex object."
101 | IDS_ALREADY_RUNNING "The program is already running."
102 | IDS_SAVE_FILE_ERROR "Fatal error: failed to create the save file."
103 | IDS_TOO_MANY_HIDDEN_WINDOWS "Too many minimized windows. Please restore some."
104 | IDS_RESTORE_FROM_UNEXPECTED_TERMINATION "Program had previously been terminated unexpectedly. Minimized %d windows."
105 | IDS_HIDING_WINDOW "Minimize window to tray"
106 | IDS_TRAY "Tray icon"
107 | IDS_MENU "Popup menu"
108 | IDS_COL_KEY "Key"
109 | IDS_COL_ACTION "Action"
110 | IDS_ACT_1 "Minimize the foreground window"
111 | IDS_ACT_2 "Popup minimized windows list"
112 | IDS_ACT_3 "Restore the last window"
113 | IDS_NEW_RULE "New Rule"
114 | IDS_UNSAVED "The rule has not been saved. Do you want to save it now?"
115 | IDS_RULE_INFO_REQUIRED "The form must be filled in completely"
116 | IDS_INVALID_REGEX """%s"" is not a valid regular expression"
117 | END
118 |
--------------------------------------------------------------------------------
/locale/en_US/en_US.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 17.0
23 | Win32Proj
24 | {ee43692d-0e29-4699-9007-7f1d41bb38d5}
25 |
26 |
27 | 10.0
28 | traymond.locale.en_US
29 |
30 |
31 |
32 | DynamicLibrary
33 |
34 |
35 | v143
36 | Unicode
37 |
38 |
39 |
40 |
41 | DynamicLibrary
42 |
43 |
44 | v143
45 |
46 |
47 | Unicode
48 |
49 |
50 |
51 | DynamicLibrary
52 | true
53 | v143
54 | Unicode
55 |
56 |
57 | DynamicLibrary
58 | false
59 | v143
60 | true
61 | Unicode
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | en_US
86 | $(SolutionDir)$(Configuration)\locale\
87 |
88 |
89 |
90 |
91 |
92 |
93 | en_US
94 | $(SolutionDir)$(Configuration)\locale\
95 |
96 |
97 |
98 |
99 | Level3
100 | true
101 | WIN32;_DEBUG;ENUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
102 | true
103 |
104 |
105 | Windows
106 |
107 |
108 |
109 |
110 | true
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | $(OutDir)$(TargetName)
120 |
121 |
122 |
123 |
124 |
125 |
126 | true
127 |
128 | ..\..\src
129 |
130 |
131 |
132 |
133 | Level3
134 | true
135 | true
136 | true
137 | WIN32;NDEBUG;ENUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
138 | true
139 |
140 |
141 | Windows
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | true
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | $(OutDir)$(TargetName)
162 |
163 |
164 |
165 |
166 |
167 | true
168 |
169 | ..\..\src
170 |
171 |
172 |
173 |
174 | Level3
175 | true
176 | _DEBUG;ENUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
177 | true
178 |
179 |
180 | Windows
181 | true
182 | false
183 |
184 |
185 |
186 |
187 | Level3
188 | true
189 | true
190 | true
191 | NDEBUG;ENUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
192 | true
193 |
194 |
195 | Windows
196 | true
197 | true
198 | true
199 | false
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/locale/en_US/en_US.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Resource Files
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Traymond.rc:
--------------------------------------------------------------------------------
1 | #pragma code_page(65001)
2 |
3 | #include "resource.h"
4 |
5 |
6 | /////////////////////////////////////////////////////////////////////////////
7 | //
8 | // Icon
9 | //
10 |
11 | IDI_TRAYMOND ICON "..\\icon\\traymond.ico"
12 |
13 |
14 | /////////////////////////////////////////////////////////////////////////////
15 | //
16 | // Version
17 | //
18 |
19 | VS_VERSION_INFO VERSIONINFO
20 | FILEVERSION VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_BUILD
21 | PRODUCTVERSION VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH
22 | FILEFLAGSMASK 0x3fL
23 | #ifdef _DEBUG
24 | FILEFLAGS 0x1L
25 | #else
26 | FILEFLAGS 0x0L
27 | #endif
28 | FILEOS VOS_NT_WINDOWS32
29 | FILETYPE VFT_APP
30 | FILESUBTYPE 0x0L
31 | BEGIN
32 | BLOCK "StringFileInfo"
33 | BEGIN
34 | BLOCK "000004b0"
35 | BEGIN
36 | VALUE "FileDescription", "Traymond"
37 | VALUE "FileVersion", MAKEFULLVERSION
38 | VALUE "InternalName", PROJECT_NAME ".exe"
39 | VALUE "OriginalFilename", PROJECT_NAME ".exe"
40 | VALUE "ProductName", PROJECT_NAME
41 | VALUE "ProductVersion", MAKEVERSION
42 | VALUE "LegalCopyright", "https://github.com/tabris17/traymond"
43 | END
44 | END
45 | BLOCK "VarFileInfo"
46 | BEGIN
47 | VALUE "Translation", LANG_NEUTRAL, 1200
48 | END
49 | END
50 |
51 |
52 | /////////////////////////////////////////////////////////////////////////////
53 | //
54 | // Menu
55 | //
56 |
57 | IDM_POPUP MENU
58 | BEGIN
59 | POPUP ""
60 | BEGIN
61 | MENUITEM "还原所有窗口", IDM_RESTORE_ALL_WINDOWS
62 | MENUITEM "还原最后一个窗口", IDM_RESTORE_LAST_WINDOW
63 | MENUITEM "选项", IDM_OPTIONS
64 | MENUITEM SEPARATOR
65 | MENUITEM "退出", IDM_EXIT
66 | END
67 | END
68 |
69 |
70 | /////////////////////////////////////////////////////////////////////////////
71 | //
72 | // Dialog
73 | //
74 |
75 | IDD_OPTIONS DIALOGEX 0, 0, 180, 204
76 | STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
77 | EXSTYLE WS_EX_STATICEDGE
78 | CAPTION "Traymond 选项"
79 | FONT 9, "MS Shell Dlg", 400, 0, 0x1
80 | BEGIN
81 | DEFPUSHBUTTON "确定",IDOK,39,184,50,14
82 | PUSHBUTTON "取消",IDCANCEL,93,184,50,14
83 | GROUPBOX "自定义热键",IDC_STATIC,6,6,168,105
84 | CONTROL "",IDC_HOTKEY_LIST,"SysListView32",LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_REPORT | WS_BORDER | WS_TABSTOP,12,18,156,66
85 | CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP | WS_DISABLED,12,90,84,14
86 | CONTROL "使用 Win 键",IDC_CHECK_USE_WIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED,102,92,58,11
87 | LTEXT "最小化窗口收纳到 ",IDC_STATIC,6,124,60,10
88 | COMBOBOX IDC_COMBO_HIDE_TYPE,70,122,66,120,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_HASSTRINGS | WS_TABSTOP
89 | CONTROL "开机时自动运行",IDC_CHECK_AUTORUN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,143,72,11
90 | CONTROL "自动将窗口最小化到托盘",IDC_CHECK_AUTO_HIDING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,159,100,11
91 | PUSHBUTTON "自定义规则",IDC_BUTTON_RULES,110,157,60,14,WS_DISABLED
92 | END
93 |
94 | IDD_RULES DIALOGEX 0, 0, 400, 306
95 | STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | DS_SETFOREGROUND | DS_SHELLFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU
96 | CAPTION "Traymond 自定义规则"
97 | FONT 9, "MS Shell Dlg", 400, 0, 0x1
98 | BEGIN
99 | DEFPUSHBUTTON "关闭",IDCANCEL,345,286,50,14,BS_FLAT
100 | PUSHBUTTON "取消",IDABORT,290,286,50,14,BS_FLAT | WS_DISABLED
101 | PUSHBUTTON "帮助",IDHELP,236,286,50,14,BS_FLAT
102 | LISTBOX IDC_LIST_RULES,5,5,135,276,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
103 | PUSHBUTTON "新建",IDC_NEW,5,286,42,14,BS_FLAT
104 | PUSHBUTTON "保存",IDC_SAVE,51,286,42,14,BS_FLAT | WS_DISABLED
105 | PUSHBUTTON "删除",IDC_REMOVE,97,286,42,14,BS_FLAT | WS_DISABLED
106 | LTEXT "规则名称:",IDC_STATIC,145,8,264,10
107 | EDITTEXT IDC_EDIT_NAME,145,20,250,14,ES_AUTOHSCROLL | WS_DISABLED
108 | LTEXT "从已最小化的窗口创建规则:",IDC_STATIC,145,40,264,10
109 | COMBOBOX IDC_COMBO_WINDOWS,145,52,250,200,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_HASSTRINGS | WS_TABSTOP | WS_DISABLED
110 | GROUPBOX "窗口标题",IDC_STATIC,145,70,250,45
111 | EDITTEXT IDC_EDIT_TEXT,153,84,235,14,ES_AUTOHSCROLL | WS_DISABLED
112 | CONTROL "正则匹配",IDC_CHECK_REGEX_TEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED,153,99,71,12
113 | GROUPBOX "窗口类名",IDC_STATIC,145,119,250,45
114 | EDITTEXT IDC_EDIT_CLASS,153,133,235,14,ES_AUTOHSCROLL | WS_DISABLED
115 | CONTROL "正则匹配",IDC_CHECK_REGEX_CLASS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED,153,148,71,12
116 | GROUPBOX "程序路径",IDC_STATIC,145,168,250,45
117 | EDITTEXT IDC_EDIT_PATH,153,182,235,14,ES_AUTOHSCROLL | WS_DISABLED
118 | CONTROL "正则匹配",IDC_CHECK_REGEX_PATH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED,153,197,71,12
119 | GROUPBOX "生效场景",IDC_STATIC,145,217,250,30
120 | CONTROL "窗口首次出现时",IDC_RADIO_ON_FIRST_SHOW,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_DISABLED,153,229,65,13
121 | CONTROL "窗口最小化时",IDC_RADIO_ON_MINIMIZE,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,218,229,60,13
122 | CONTROL "两者",IDC_RADIO_ON_BOTH,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,278,229,33,13
123 | GROUPBOX "显示通知",IDC_STATIC,145,251,250,30
124 | CONTROL "从不",IDC_RADIO_NEVER_NOTIFY,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_DISABLED,153,263,28,13
125 | CONTROL "总是",IDC_RADIO_ALWAYS_NOTIFY,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,183,263,28,13
126 | CONTROL "窗口首次最小化时",IDC_RADIO_NOTIFY_FIRST_TIME,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,213,263,100,13
127 | END
128 |
129 | IDD_ICONS DIALOGEX 0, 0, 120, 100
130 | STYLE DS_SETFONT | DS_CENTERMOUSE | DS_SETFOREGROUND | DS_SHELLFONT | WS_POPUP
131 | FONT 12, "MS Shell Dlg", 400, 0, 0x1
132 | CLASS POPUP_CLASS
133 | BEGIN
134 | CONTROL "",IDC_ICON_LIST,"SysListView32",LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_REPORT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,0,0,120,100
135 | END
136 |
137 |
138 | /////////////////////////////////////////////////////////////////////////////
139 | //
140 | // String Table
141 | //
142 |
143 | STRINGTABLE
144 | BEGIN
145 | IDS_HOTKEY_ERROR "无法注册系统热键 %s,可能已被占用。\n请选择其他组合键。"
146 | IDS_MUTEX_ERROR "创建互斥对象失败,无法启动程序。"
147 | IDS_ALREADY_RUNNING "程序已在运行中。"
148 | IDS_SAVE_FILE_ERROR "无法创建保存文件。"
149 | IDS_TOO_MANY_HIDDEN_WINDOWS "隐藏太多窗口,请先释放一些。"
150 | IDS_RESTORE_FROM_UNEXPECTED_TERMINATION "程序先前意外终止。已恢复 %d 个隐藏窗口。"
151 | IDS_HIDING_WINDOW "最小化窗口至托盘"
152 | IDS_TRAY "托盘图标"
153 | IDS_MENU "右键菜单"
154 | IDS_COL_KEY "按键"
155 | IDS_COL_ACTION "行为"
156 | IDS_ACT_1 "最小化前台窗口"
157 | IDS_ACT_2 "弹出最小化窗口列表"
158 | IDS_ACT_3 "还原最后一个窗口"
159 | IDS_NEW_RULE "新规则"
160 | IDS_UNSAVED "当前编辑的规则未保存。是否要保存?"
161 | IDS_RULE_INFO_REQUIRED "必须输入完整的规则信息"
162 | IDS_INVALID_REGEX """%s"" 不是正经的正则表达式"
163 | END
164 |
--------------------------------------------------------------------------------
/src/i18n.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "i18n.h"
5 |
6 |
7 | bool I18n::loadLangMod()
8 | {
9 | // override default language
10 | /* if (_tcscmp(DEFAULT_LOCALE, locale) == 0) {
11 | return false;
12 | }*/
13 | TCHAR fileName[MAX_PATH];
14 | auto fileNameLength = GetModuleFileName(NULL, fileName, MAX_PATH);
15 | if (fileNameLength == 0 || fileNameLength >= MAX_PATH) {
16 | return false;
17 | }
18 | for (int i = fileNameLength - 1; i >= 0; i--) {
19 | if (fileName[i] == _T("\\")[0]) {
20 | constexpr TCHAR dotTail[] = _T(".");
21 | fileName[i + 1] = _T("")[0];
22 | if (
23 | _tcsncat_s(fileName, MAX_PATH, LOCALE_DIR, _countof(LOCALE_DIR)) != 0 ||
24 | _tcsncat_s(fileName, MAX_PATH, locale, MAX_LOCALE) != 0 ||
25 | _tcsncat_s(fileName, MAX_PATH, dotTail, _countof(dotTail))
26 | ) {
27 | return false;
28 | }
29 | langMod = LoadLangMod(fileName);
30 | if (langMod) {
31 | return true;
32 | }
33 | fileName[i + 1] = _T("")[0];
34 | if (
35 | _tcsncat_s(fileName, MAX_PATH, LOCALE_DIR, _countof(LOCALE_DIR)) != 0 ||
36 | _tcsncat_s(fileName, MAX_PATH, fallback, MAX_LOCALE) != 0 ||
37 | _tcsncat_s(fileName, MAX_PATH, dotTail, _countof(dotTail))
38 | ) {
39 | return false;
40 | }
41 | langMod = LoadLangMod(fileName);
42 | if (langMod) {
43 | return true;
44 | }
45 | break;
46 | }
47 | }
48 | return false;
49 | }
50 |
51 | bool I18n::getLocale()
52 | {
53 | constexpr auto MAX_CTRY = 4;
54 | TCHAR country[MAX_CTRY]{ NULL };
55 | if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, fallback, MAX_LOCALE) == 0) {
56 | return false;
57 | }
58 | if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, MAX_CTRY) == 0) {
59 | return false;
60 | }
61 | _stprintf_s(locale, _T("%s_%s"), fallback, country);
62 | return true;
63 | }
64 |
65 | I18n::I18n()
66 | {
67 | if (getLocale()) {
68 | loadLangMod();
69 | }
70 |
71 | for (int i = 0; i < _countof(stringTable); i++) {
72 | LoadString(langMod, IDS_BEGIN + i, reinterpret_cast(stringTable + i), 0);
73 | }
74 | }
75 |
76 | LPTSTR I18n::operator[](int stringId)
77 | {
78 | if (stringId < IDS_BEGIN || stringId >= IDS_END) {
79 | return nullptr;
80 | }
81 | return stringTable[stringId - IDS_BEGIN];
82 | }
83 |
84 | HINSTANCE I18n::lang(HINSTANCE defaultInstance = NULL)
85 | {
86 | if (langMod == NULL) {
87 | return defaultInstance;
88 | }
89 | return langMod;
90 | }
91 |
--------------------------------------------------------------------------------
/src/i18n.h:
--------------------------------------------------------------------------------
1 | #include "resource.h"
2 |
3 | #ifndef I18N_H
4 | #define I18N_H
5 | #define MAX_LOCALE 10
6 | #define DEFAULT_LOCALE _T("zh_CN")
7 | #define LOCALE_DIR _T("locale\\")
8 | #define LoadLangMod(lmfn) LoadLibraryEx(lmfn, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE)
9 |
10 | class I18n {
11 | private:
12 | TCHAR locale[MAX_LOCALE]{};
13 | TCHAR fallback[MAX_LOCALE]{};
14 | LPTSTR stringTable[IDS_MAX_SIZE]{};
15 | HINSTANCE langMod = NULL;
16 | bool loadLangMod();
17 | bool getLocale();
18 | public:
19 | I18n();
20 | LPTSTR operator[](int stringId);
21 | HINSTANCE lang(HINSTANCE defaultInstance);
22 | };
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/src/icons.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "resource.h"
7 | #include "icons.h"
8 | #include "logging.h"
9 | #include "traymond.h"
10 |
11 |
12 | static BOOL initDialog(HWND hwnd, IconsDialog* state)
13 | {
14 | LVITEM lvi{};
15 | LVCOLUMN lvc{};
16 |
17 | auto context = state->getContext();
18 | auto listView = GetDlgItem(hwnd, IDC_ICON_LIST);
19 | auto imageList = state->getImageList();
20 |
21 | SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
22 | SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(state));
23 | ListView_SetExtendedListViewStyle(listView, LVS_EX_BORDERSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_AUTOSIZECOLUMNS | LVS_EX_FULLROWSELECT | LVS_EX_TRACKSELECT);
24 | ListView_SetHoverTime(listView, 0);
25 | ListView_SetImageList(listView, imageList, LVSIL_NORMAL);
26 | ListView_SetImageList(listView, imageList, LVSIL_SMALL);
27 |
28 | RECT rect{};
29 | GetClientRect(listView, &rect);
30 | lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
31 | lvc.fmt = LVCFMT_LEFT;
32 | lvc.cx = rect.right;
33 | lvc.iSubItem = 0;
34 | ListView_InsertColumn(listView, 0, &lvc);
35 |
36 | lvi.mask = LVIF_TEXT | LVIF_IMAGE;
37 | for (int i = 0; i < context->iconIndex; i++) {
38 | TCHAR text[MAX_WINDOW_TEXT]{};
39 | HWND hiddenWindow = context->icons[i].window;
40 | GetWindowText(hiddenWindow, text, MAX_WINDOW_TEXT);
41 | ImageList_AddIcon(imageList, getWindowIcon(context, hiddenWindow));
42 | lvi.iItem = i;
43 | lvi.pszText = text;
44 | lvi.iImage = i;
45 | ListView_InsertItem(listView, &lvi);
46 | }
47 |
48 | return TRUE;
49 | }
50 |
51 |
52 | static BOOL restoreSelectedWindow(HWND hwnd)
53 | {
54 | TRCONTEXT* context = IconList_GetContext(hwnd);
55 | int selectedIndex = IconList_GetSelectedIndex(hwnd);
56 |
57 | if (selectedIndex >= 0 && selectedIndex < context->iconIndex) {
58 | restoreWindow(context, 0, context->icons[selectedIndex].window);
59 | return EndDialog(hwnd, IDOK);
60 | }
61 |
62 | return FALSE;
63 | }
64 |
65 |
66 | static LRESULT CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
67 | {
68 | switch (uMsg) {
69 | case WM_COMMAND:
70 | switch (LOWORD(wParam)) {
71 | case IDCANCEL:
72 | return EndDialog(hwnd, IDCANCEL);
73 | case IDOK:
74 | return restoreSelectedWindow(hwnd);
75 | }
76 | break;
77 | case WM_NOTIFY:
78 | if (wParam == IDC_ICON_LIST &&
79 | reinterpret_cast(lParam)->code == NM_CLICK) {
80 |
81 | return restoreSelectedWindow(hwnd);
82 | }
83 | break;
84 | case WM_NCACTIVATE:
85 | if (wParam == 0 && IsWindowVisible(hwnd)) {
86 | return EndDialog(hwnd, IDCANCEL);
87 | }
88 | break;
89 | case WM_INITDIALOG:
90 | return initDialog(hwnd, reinterpret_cast(lParam));
91 | }
92 | return DefWindowProc(hwnd, uMsg, wParam, lParam);
93 | }
94 |
95 | INT_PTR showIconsDlg(TRCONTEXT *context)
96 | {
97 | static bool dialogOpened = false;
98 | if (dialogOpened || context->iconIndex == 0) {
99 | return FALSE;
100 | }
101 | dialogOpened = true;
102 | auto state = IconsDialog(context);
103 | auto result = DialogBoxParam(
104 | context->instance,
105 | MAKEINTRESOURCE(IDD_ICONS),
106 | HWND_DESKTOP,
107 | (DLGPROC)DialogProc,
108 | (LPARAM)&state
109 | );
110 | dialogOpened = false;
111 | return result;
112 | }
113 |
114 | IconsDialog::IconsDialog(TRCONTEXT* context)
115 | {
116 | this->context = context;
117 | auto cx = GetSystemMetrics(SM_CXSMICON),
118 | cy = GetSystemMetrics(SM_CYSMICON);
119 | imageList = ImageList_Create(cx, cy, ILC_COLORDDB, context->iconIndex, 1);
120 | }
121 |
122 | IconsDialog::~IconsDialog()
123 | {
124 | ImageList_Destroy(imageList);
125 | }
126 |
127 | TRCONTEXT* IconsDialog::getContext()
128 | {
129 | return context;
130 | }
131 |
132 | HIMAGELIST IconsDialog::getImageList()
133 | {
134 | return imageList;
135 | }
136 |
--------------------------------------------------------------------------------
/src/icons.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "traymond.h"
5 |
6 | #define IconList_GetSelectedIndex(hIconsDialog) \
7 | ListView_GetNextItem(GetDlgItem(hIconsDialog, IDC_ICON_LIST), -1, LVNI_SELECTED)
8 |
9 | #define IconList_GetContext(hIconsDialog) \
10 | reinterpret_cast(GetWindowLongPtr(hIconsDialog, GWLP_USERDATA))->getContext()
11 |
12 |
13 | class IconsDialog final {
14 | private:
15 | TRCONTEXT* context;
16 | HIMAGELIST imageList;
17 | public:
18 | IconsDialog(TRCONTEXT* context);
19 | ~IconsDialog();
20 | TRCONTEXT* getContext();
21 | HIMAGELIST getImageList();
22 | };
23 |
24 | INT_PTR showIconsDlg(TRCONTEXT* context);
25 |
--------------------------------------------------------------------------------
/src/logging.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #ifdef DEBUG
5 | #define __LOGGING__ AllocConsole();\
6 | auto _logging_stdout = freopen("CONOUT$", "w", stdout)
7 | #define debugf(...) printf(__VA_ARGS__)
8 | #else
9 | #define __LOGGING__
10 | #define debugf(...)
11 | #endif
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/options.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "resource.h"
7 | #include "logging.h"
8 | #include "traymond.h"
9 | #include "options.h"
10 | #include "rules.h"
11 |
12 |
13 | UINT HotkeyToMod(UINT fsModifiers)
14 | {
15 | if ((fsModifiers & HOTKEYF_SHIFT) && !(fsModifiers & HOTKEYF_ALT)) {
16 | fsModifiers &= ~HOTKEYF_SHIFT;
17 | fsModifiers |= MOD_SHIFT;
18 | }
19 | else if (!(fsModifiers & HOTKEYF_SHIFT) && (fsModifiers & HOTKEYF_ALT)) {
20 | fsModifiers &= ~HOTKEYF_ALT;
21 | fsModifiers |= MOD_ALT;
22 | }
23 | return fsModifiers;
24 | }
25 |
26 |
27 | UINT ModToHotkey(UINT fsModifiers)
28 | {
29 | if ((fsModifiers & MOD_SHIFT) && !(fsModifiers & MOD_ALT)) {
30 | fsModifiers &= ~MOD_SHIFT;
31 | fsModifiers |= HOTKEYF_SHIFT;
32 | } else if (!(fsModifiers & MOD_SHIFT) && (fsModifiers & MOD_ALT)) {
33 | fsModifiers &= ~MOD_ALT;
34 | fsModifiers |= HOTKEYF_ALT;
35 | }
36 | return fsModifiers;
37 | }
38 |
39 |
40 | static void loadHotkey(PTCHAR key, HOTKEY *hotkey)
41 | {
42 | DWORD data = 0, size = sizeof(DWORD);
43 | if (ERROR_SUCCESS == RegGetValue(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, key, RRF_RT_REG_DWORD, NULL, &data, &size)) {
44 | auto vkey = LOWORD(data), modifiers = HIWORD(data);
45 | if (vkey > 0 && modifiers > 0) {
46 | hotkey->modifiers = modifiers;
47 | hotkey->vkey = vkey;
48 | }
49 | }
50 | }
51 |
52 |
53 | void loadOptions(TRCONTEXT* context)
54 | {
55 | context->hotkey.modifiers = MOD_KEY;
56 | context->hotkey.vkey = TRAY_KEY;
57 | context->hotkey2.modifiers = 0;
58 | context->hotkey2.vkey = 0;
59 | context->hotkey3.modifiers = 0;
60 | context->hotkey3.vkey = 0;
61 | context->autorun = FALSE;
62 | context->hideType = HideTray;
63 | context->autoHiding = FALSE;
64 | context->hook = NULL;
65 |
66 | loadHotkey(_T("Hotkey"), &context->hotkey);
67 | loadHotkey(_T("Hotkey2"), &context->hotkey2);
68 | loadHotkey(_T("Hotkey3"), &context->hotkey3);
69 |
70 | DWORD data = 0, size = sizeof(DWORD);
71 |
72 | if (ERROR_SUCCESS == RegGetValue(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, _T("HideType"), RRF_RT_REG_DWORD, NULL, &data, &size)) {
73 | context->hideType = data ? HideMenu : HideTray;
74 | }
75 |
76 | if (ERROR_SUCCESS == RegGetValue(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, _T("AutoHiding"), RRF_RT_REG_DWORD, NULL, &data, &size)) {
77 | context->autoHiding = (BOOL)data;
78 | }
79 |
80 | if (ERROR_SUCCESS == RegGetValue(HKEY_CURRENT_USER, REG_KEY_RUN, APP_NAME, RRF_RT_REG_SZ, NULL, NULL, NULL)) {
81 | context->autorun = TRUE;
82 | }
83 | }
84 |
85 |
86 | void saveOptions(TRCONTEXT* context)
87 | {
88 | HKEY regKey = NULL;
89 |
90 | if (ERROR_SUCCESS == RegCreateKey(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, ®Key)) {
91 | DWORD data = MAKELONG(context->hotkey.vkey, context->hotkey.modifiers);
92 | RegSetValueEx(regKey, _T("Hotkey"), 0, REG_DWORD, (BYTE*)&data, sizeof(DWORD));
93 | data = MAKELONG(context->hotkey2.vkey, context->hotkey2.modifiers);
94 | RegSetValueEx(regKey, _T("Hotkey2"), 0, REG_DWORD, (BYTE*)&data, sizeof(DWORD));
95 | data = MAKELONG(context->hotkey3.vkey, context->hotkey3.modifiers);
96 | RegSetValueEx(regKey, _T("Hotkey3"), 0, REG_DWORD, (BYTE*)&data, sizeof(DWORD));
97 | data = context->hideType;
98 | RegSetValueEx(regKey, _T("HideType"), 0, REG_DWORD, (BYTE*)&data, sizeof(DWORD));
99 | data = context->autoHiding;
100 | RegSetValueEx(regKey, _T("AutoHiding"), 0, REG_DWORD, (BYTE*)&data, sizeof(DWORD));
101 | RegCloseKey(regKey);
102 | }
103 |
104 | if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, REG_KEY_RUN, ®Key)) {
105 | if (context->autorun) {
106 | RegSetValueEx(regKey, APP_NAME, 0, REG_SZ, (BYTE*)context->cmdLine, _tcslen(context->cmdLine) * sizeof(TCHAR));
107 | }
108 | else {
109 | RegDeleteValue(regKey, APP_NAME);
110 | }
111 | RegCloseKey(regKey);
112 | }
113 | }
114 |
115 |
116 | static BOOL setOptions(HWND hwnd, TRCONTEXT* context, WPARAM wParam)
117 | {
118 | UINT hotkeyIds[] = { IDHOT_HIDE_WINDOW, IDHOT_POPUP_ICONS, IDHOT_RESTORE_LAST_WINDOW };
119 | HOTKEY* hotkeys[] = { &context->hotkey, &context->hotkey2, &context->hotkey3 };
120 | HOTKEY readHotkeys[_countof(hotkeyIds)]{0};
121 | auto listView = GetDlgItem(hwnd, IDC_HOTKEY_LIST);
122 | LVITEM lvi{};
123 | lvi.mask = LVIF_PARAM;
124 | lvi.iSubItem = 0;
125 | for (int i = 0; i < _countof(readHotkeys); i++) {
126 | auto readHotkey = &readHotkeys[i];
127 | auto hotkey = hotkeys[i];
128 | lvi.iItem = i;
129 | if (ListView_GetItem(listView, &lvi)) {
130 | readHotkey->modifiers = HotkeyToMod(HIBYTE(LOWORD(lvi.lParam)));
131 | readHotkey->vkey = LOBYTE(LOWORD(lvi.lParam));
132 | if (hotkey->modifiers == readHotkey->modifiers && hotkey->vkey == readHotkey->vkey) {
133 | hotkeys[i] = nullptr;
134 | continue;
135 | }
136 | if (lvi.lParam == 0) {
137 | continue;
138 | }
139 | if (!tryRegisterHotkey(hwnd, TEST_HOTKEY_ID, readHotkey->modifiers, readHotkey->vkey)) {
140 | return FALSE;
141 | }
142 | UnregisterHotKey(hwnd, TEST_HOTKEY_ID);
143 | }
144 | }
145 | for (int i = 0; i < _countof(hotkeys); i++) {
146 | auto hotkeyId = hotkeyIds[i];
147 | auto readHotkey = readHotkeys[i];
148 | auto hotkey = hotkeys[i];
149 | if (hotkey == nullptr) {
150 | continue;
151 | }
152 | UnregisterHotKey(context->mainWindow, hotkeyId);
153 | if (readHotkey.modifiers > 0 && readHotkey.vkey > 0) {
154 | RegisterHotKey(context->mainWindow, hotkeyId, readHotkey.modifiers | MOD_NOREPEAT, readHotkey.vkey);
155 | }
156 | hotkey->modifiers = readHotkey.modifiers;
157 | hotkey->vkey = readHotkey.vkey;
158 | }
159 | context->autorun = IsDlgButtonChecked(hwnd, IDC_CHECK_AUTORUN);
160 | context->autoHiding = IsDlgButtonChecked(hwnd, IDC_CHECK_AUTO_HIDING);
161 | context->hideType = ComboBox_GetCurSel(GetDlgItem(hwnd, IDC_COMBO_HIDE_TYPE)) ? HideMenu : HideTray;
162 | reviseHiddenWindowIcon(context);
163 | saveOptions(context);
164 | return EndDialog(hwnd, wParam);
165 | }
166 |
167 |
168 | static BOOL initDialog(HWND hwnd, TRCONTEXT* context)
169 | {
170 | SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(context));
171 | SendMessage(hwnd, WM_SETICON, TRUE, (LPARAM)context->mainIcon);
172 | SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM)context->mainIcon);
173 |
174 | auto listView = GetDlgItem(hwnd, IDC_HOTKEY_LIST);
175 | ListView_SetExtendedListViewStyle(listView, LVS_EX_BORDERSELECT | LVS_EX_FULLROWSELECT | LVS_EX_AUTOSIZECOLUMNS);
176 | LVCOLUMN lvc{};
177 | lvc.mask = LVCF_FMT | LVCF_MINWIDTH | LVCF_TEXT;
178 | lvc.fmt = LVCFMT_LEFT;
179 | lvc.cxMin = 120;
180 | lvc.pszText = i18n[IDS_COL_KEY];
181 | ListView_InsertColumn(listView, 0, &lvc);
182 | lvc.mask = LVCF_FMT | LVCF_MINWIDTH | LVCF_TEXT;
183 | lvc.fmt = LVCFMT_LEFT;
184 | lvc.cxMin = 120;
185 | lvc.pszText = i18n[IDS_COL_ACTION];
186 | ListView_InsertColumn(listView, 1, &lvc);
187 |
188 | TCHAR hotkeyText[MAX_HOTKEY_TEXT]{ NULL };
189 | PTCHAR actions[] = { i18n[IDS_ACT_1], i18n[IDS_ACT_2], i18n[IDS_ACT_3] };
190 | LVITEM lvi{};
191 | lvi.mask = LVIF_TEXT;
192 | for (int i = 0; i < _countof(actions); i++) {
193 | lvi.iSubItem = 0;
194 | lvi.iItem = i;
195 | lvi.pszText = NULL;
196 | ListView_InsertItem(listView, &lvi);
197 | lvi.iSubItem = 1;
198 | lvi.pszText = actions[i];
199 | ListView_SetItem(listView, &lvi);
200 | }
201 |
202 | HOTKEY* hotkeys[] = { &context->hotkey, &context->hotkey2, &context->hotkey3 };
203 | lvi.mask = LVIF_TEXT | LVIF_PARAM;
204 | lvi.iSubItem = 0;
205 | for (int i = 0; i < _countof(hotkeys); i++) {
206 | auto hotkey = hotkeys[i];
207 | if (hotkey->modifiers == 0 || hotkey->vkey == 0) {
208 | continue;
209 | }
210 | getHotkeyText(hotkeyText, _countof(hotkeyText), hotkey->modifiers, hotkey->vkey);
211 | lvi.pszText = hotkeyText;
212 | lvi.iItem = i;
213 | lvi.lParam = MAKEWORD(hotkey->vkey, ModToHotkey(hotkey->modifiers));
214 | ListView_SetItem(listView, &lvi);
215 | }
216 | ListView_SetColumnWidth(listView, 0, LVSCW_AUTOSIZE);
217 | ListView_SetColumnWidth(listView, 1, LVSCW_AUTOSIZE);
218 |
219 | CheckDlgButton(hwnd, IDC_CHECK_AUTORUN, context->autorun);
220 | CheckDlgButton(hwnd, IDC_CHECK_AUTO_HIDING, context->autoHiding);
221 | Button_Enable(GetDlgItem(hwnd, IDC_BUTTON_RULES), context->autoHiding);
222 |
223 | auto hideTypeCombo = GetDlgItem(hwnd, IDC_COMBO_HIDE_TYPE);
224 | ComboBox_AddItemData(hideTypeCombo, i18n[IDS_TRAY]);
225 | ComboBox_AddItemData(hideTypeCombo, i18n[IDS_MENU]);
226 | ComboBox_SetCurSel(hideTypeCombo, context->hideType);
227 |
228 | return TRUE;
229 | }
230 |
231 |
232 | static BOOL onHotkeyListItemChanged(HWND hwndDlg)
233 | {
234 | auto hotkeyListView = GetDlgItem(hwndDlg, IDC_HOTKEY_LIST);
235 | auto hotkeyEdit = GetDlgItem(hwndDlg, IDC_HOTKEY);
236 | auto hotkeyUseWinCheck = GetDlgItem(hwndDlg, IDC_CHECK_USE_WIN);
237 | auto selectedIndex = ListView_GetNextItem(GetDlgItem(hwndDlg, IDC_HOTKEY_LIST), -1, LVNI_SELECTED);
238 | if (selectedIndex < 0) {
239 | return FALSE;
240 | }
241 |
242 | LVITEM lvi{};
243 | lvi.mask = LVIF_PARAM;
244 | lvi.iItem = selectedIndex;
245 | lvi.iSubItem = 0;
246 | if (!ListView_GetItem(hotkeyListView, &lvi)) {
247 | return FALSE;
248 | }
249 |
250 | EnableWindow(hotkeyEdit, TRUE);
251 | EnableWindow(hotkeyUseWinCheck, TRUE);
252 |
253 | SendMessage(hotkeyEdit, HKM_SETHOTKEY, lvi.lParam, 0);
254 |
255 | auto modifers = HotkeyToMod(HIBYTE(LOWORD(lvi.lParam)));
256 | return CheckDlgButton(hwndDlg, IDC_CHECK_USE_WIN, (modifers & MOD_WIN) ? BST_CHECKED : BST_UNCHECKED);
257 | }
258 |
259 |
260 | static BOOL onHotkeyChanged(HWND hwndDlg)
261 | {
262 | auto hotkeyListView = GetDlgItem(hwndDlg, IDC_HOTKEY_LIST);
263 | auto hotkeyEdit = GetDlgItem(hwndDlg, IDC_HOTKEY);
264 | auto selectedIndex = ListView_GetNextItem(GetDlgItem(hwndDlg, IDC_HOTKEY_LIST), -1, LVNI_SELECTED);
265 | if (selectedIndex < 0) {
266 | return FALSE;
267 | }
268 |
269 | UINT vkey, modifiers;
270 | DWORD result = SendMessage(hotkeyEdit, HKM_GETHOTKEY, 0, 0);
271 | vkey = LOBYTE(LOWORD(result));
272 | modifiers = HotkeyToMod(HIBYTE(LOWORD(result)));
273 | if (BST_UNCHECKED != IsDlgButtonChecked(hwndDlg, IDC_CHECK_USE_WIN)) {
274 | modifiers |= MOD_WIN;
275 | }
276 | else {
277 | modifiers &= ~MOD_WIN;
278 | }
279 |
280 | if (vkey + modifiers > 0 && (vkey == 0 || modifiers == 0)) {
281 | return FALSE;
282 | }
283 | TCHAR hotkeyText[MAX_HOTKEY_TEXT]{ NULL };
284 | LVITEM lvi{};
285 | lvi.mask = LVIF_TEXT | LVIF_PARAM;
286 | lvi.iSubItem = 0;
287 | lvi.pszText = hotkeyText;
288 | lvi.iItem = selectedIndex;
289 | getHotkeyText(hotkeyText, _countof(hotkeyText), modifiers, vkey);
290 | lvi.lParam = MAKEWORD(vkey, ModToHotkey(modifiers));
291 | ListView_SetItem(hotkeyListView, &lvi);
292 | return TRUE;
293 | }
294 |
295 |
296 | static BOOL CALLBACK DialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
297 | {
298 | TRCONTEXT* context = reinterpret_cast(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
299 | switch (message) {
300 | case WM_COMMAND:
301 | switch (LOWORD(wParam)) {
302 | case IDOK:
303 | return setOptions(hwndDlg, context, wParam);
304 | case IDCANCEL:
305 | return EndDialog(hwndDlg, wParam);
306 | case IDC_CHECK_AUTO_HIDING:
307 | return Button_Enable(GetDlgItem(hwndDlg, IDC_BUTTON_RULES), IsDlgButtonChecked(hwndDlg, IDC_CHECK_AUTO_HIDING));
308 | case IDC_BUTTON_RULES:
309 | showRulesDlg(hwndDlg, context);
310 | return TRUE;
311 | case IDC_HOTKEY:
312 | if (HIWORD(wParam) == EN_CHANGE) {
313 | return onHotkeyChanged(hwndDlg);
314 | }
315 | break;
316 | case IDC_CHECK_USE_WIN:
317 | return onHotkeyChanged(hwndDlg);
318 | }
319 | break;
320 | case WM_NOTIFY:
321 | switch (LOWORD(wParam)) {
322 | case IDC_HOTKEY_LIST:
323 | if (LVN_ITEMCHANGED == reinterpret_cast(lParam)->code &&
324 | (reinterpret_cast(lParam)->uChanged & LVIF_STATE) &&
325 | (reinterpret_cast(lParam)->uNewState & LVIS_SELECTED)) {
326 |
327 | onHotkeyListItemChanged(hwndDlg);
328 | }
329 | break;
330 | }
331 | break;
332 | case WM_INITDIALOG:
333 | return initDialog(hwndDlg, reinterpret_cast(lParam));
334 | }
335 | return FALSE;
336 | }
337 |
338 |
339 | INT_PTR showOptionsDlg(TRCONTEXT* context)
340 | {
341 | static bool dialogOpened = false;
342 | if (dialogOpened) {
343 | return FALSE;
344 | }
345 |
346 | dialogOpened = true;
347 | auto result = DialogBoxParam(
348 | i18n.lang(context->instance),
349 | MAKEINTRESOURCE(IDD_OPTIONS),
350 | HWND_DESKTOP,
351 | (DLGPROC)DialogProc,
352 | (LPARAM)context
353 | );
354 | dialogOpened = false;
355 | return result;
356 | }
357 |
--------------------------------------------------------------------------------
/src/options.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | INT_PTR showOptionsDlg(TRCONTEXT* context);
4 | void loadOptions(TRCONTEXT* context);
5 | void saveOptions(TRCONTEXT* context);
6 | UINT HotkeyToMod(UINT fsModifiers);
7 | UINT ModToHotkey(UINT fsModifiers);
8 |
9 | typedef enum {
10 | HOTKEY_OPTIONS_IGNORE,
11 | HOTKEY_OPTIONS_INVALID,
12 | HOTKEY_OPTIONS_SUCCESS,
13 | } HOTKEY_OPTIONS;
14 |
--------------------------------------------------------------------------------
/src/resource.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #define PROJECT_NAME "Traymond"
4 | #define PROJECT_NAME_LC "traymond"
5 | #define PROJECT_NAME_UC "TRAYMOND"
6 | #define VERSION_MAJOR 2
7 | #define VERSION_MINOR 2
8 | #define VERSION_PATCH 0
9 | #define VERSION_BUILD 1
10 | #define _VERSTR(number) __VERSTR(number)
11 | #define __VERSTR(number) #number
12 | #define MAKEVERSION _VERSTR(VERSION_MAJOR) "." _VERSTR(VERSION_MINOR) "." _VERSTR(VERSION_PATCH)
13 | #define MAKEFULLVERSION MAKEVERSION "." _VERSTR(VERSION_BUILD)
14 | #define POPUP_CLASS "POPUP_WINDOW"
15 |
16 | #define IDC_STATIC -1
17 | #define IDM_EXIT 1
18 | #define IDM_RESTORE_ALL_WINDOWS 2
19 | #define IDM_OPTIONS 3
20 | #define IDM_RESTORE_LAST_WINDOW 4
21 | #define IDC_NEW 11
22 | #define IDC_SAVE 12
23 | #define IDC_REMOVE 13
24 | #define IDD_OPTIONS 101
25 | #define IDD_RULES 103
26 | #define IDD_ICONS 104
27 | #define IDI_TRAYMOND 201
28 | #define IDC_HOTKEY 1000
29 | #define IDC_HOTKEY_LIST 1001
30 | #define IDC_CHECK_USE_WIN 1002
31 | #define IDC_CHECK_AUTORUN 1003
32 | #define IDC_COMBO_HIDE_TYPE 1004
33 | #define IDM_POPUP 1005
34 | #define IDC_LIST_RULES 1005
35 | #define IDC_CHECK_AUTO_HIDING 1006
36 | #define IDC_BUTTON_RULES 1007
37 | #define IDC_EDIT_TEXT 1009
38 | #define IDC_EDIT_CLASS 1010
39 | #define IDC_EDIT_PATH 1011
40 | #define IDC_CHECK_REGEX_TEXT 1012
41 | #define IDC_CHECK_REGEX_CLASS 1013
42 | #define IDC_CHECK_REGEX_PATH 1014
43 | #define IDC_EDIT_NAME 1015
44 | #define IDC_COMBO_WINDOWS 1016
45 | #define IDC_ICON_LIST 1019
46 | #define IDC_RADIO_ON_MINIMIZE 1021
47 | #define IDC_RADIO_ON_FIRST_SHOW 1022
48 | #define IDC_RADIO_ON_BOTH 1023
49 | #define IDC_RADIO_NEVER_NOTIFY 1024
50 | #define IDC_RADIO_ALWAYS_NOTIFY 1025
51 | #define IDC_RADIO_NOTIFY_FIRST_TIME 1026
52 |
53 | #define IDS_BEGIN 2001
54 | #define IDS_HOTKEY_ERROR 2001
55 | #define IDS_MUTEX_ERROR 2002
56 | #define IDS_ALREADY_RUNNING 2003
57 | #define IDS_SAVE_FILE_ERROR 2004
58 | #define IDS_TOO_MANY_HIDDEN_WINDOWS 2005
59 | #define IDS_RESTORE_FROM_UNEXPECTED_TERMINATION 2006
60 | #define IDS_HIDING_WINDOW 2007
61 | #define IDS_TRAY 2008
62 | #define IDS_MENU 2009
63 | #define IDS_COL_KEY 2010
64 | #define IDS_COL_ACTION 2011
65 | #define IDS_ACT_1 2012
66 | #define IDS_ACT_2 2013
67 | #define IDS_ACT_3 2014
68 | #define IDS_NEW_RULE 2015
69 | #define IDS_UNSAVED 2016
70 | #define IDS_RULE_INFO_REQUIRED 2017
71 | #define IDS_INVALID_REGEX 2018
72 | #define IDS_END 2019
73 | #define IDS_MAX_SIZE (IDS_END - IDS_BEGIN)
74 |
--------------------------------------------------------------------------------
/src/rules.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "resource.h"
6 | #include "traymond.h"
7 | #include "winevent.h"
8 | #include "rules.h"
9 |
10 | static bool serializeRules(HIDING_RULES* rules, LPBYTE data, _Inout_ size_t* size)
11 | {
12 | size_t realSize = 0;
13 |
14 | for (HIDING_RULE* rule : *rules) {
15 | realSize += rule->size;
16 | }
17 |
18 | if (data) {
19 | if (realSize > *size) {
20 | return false;
21 | }
22 | auto offset = data;
23 | for (HIDING_RULE* rule : *rules) {
24 | memcpy(offset, rule, rule->size);
25 | offset += rule->size;
26 | }
27 | }
28 | else {
29 | *size = realSize;
30 | }
31 | return true;
32 | }
33 |
34 | static bool unserializeRules(HIDING_RULES* rules, LPBYTE data, size_t size)
35 | {
36 | HIDING_RULE* rule = NULL;
37 |
38 | for (size_t i = 0; i < size; i += rule->size) {
39 | rule = (HIDING_RULE*)(data + i);
40 | if (i + rule->size > size) {
41 | return false;
42 | }
43 | HIDING_RULE* ruleDest = (HIDING_RULE*) new BYTE[rule->size];
44 | memcpy(ruleDest, rule, rule->size);
45 | rules->push_back(ruleDest);
46 | }
47 | return true;
48 | }
49 |
50 | bool saveRules(TRCONTEXT* context)
51 | {
52 | bool result = false;
53 | HKEY regKey = NULL;
54 |
55 | if (ERROR_SUCCESS == RegCreateKey(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, ®Key)) {
56 | size_t rulesSize = 0;
57 | if (serializeRules(&context->hidingRules, NULL, &rulesSize)) {
58 | BYTE* rules = new BYTE[rulesSize];
59 | if (serializeRules(&context->hidingRules, rules, &rulesSize)) {
60 | result = ERROR_SUCCESS == RegSetValueEx(regKey, _T("Rules"), 0, REG_BINARY, rules, rulesSize);
61 | }
62 | delete[] rules;
63 | }
64 | RegCloseKey(regKey);
65 | }
66 | return result;
67 | }
68 |
69 | bool loadRules(TRCONTEXT* context)
70 | {
71 | bool result = false;
72 | DWORD dataSize = 0;
73 |
74 | clearRules(context);
75 |
76 | if (ERROR_SUCCESS == RegGetValue(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, _T("Rules"), RRF_RT_REG_BINARY, NULL, NULL, &dataSize)) {
77 | BYTE* data = new BYTE[dataSize];
78 | if (ERROR_SUCCESS == RegGetValue(HKEY_CURRENT_USER, REG_KEY_SOFTWARE, _T("Rules"), RRF_RT_REG_BINARY, NULL, data, &dataSize)) {
79 | result = unserializeRules(&context->hidingRules, data, dataSize);
80 | }
81 | delete[] data;
82 | }
83 | return result;
84 | }
85 |
86 | static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
87 | auto context = reinterpret_cast(lParam);
88 | HIDING_RULE *rule = nullptr;
89 | if (IsWindowVisible(hwnd) && matchRule(context, hwnd, false , &rule)) {
90 | minimizeWindow(context, hwnd);
91 | if (RULE_IS_ALWAYS_NOTIFY(rule->flag) ||
92 | RULE_IS_NOTIFY_FIRST_TIME(rule->flag)) {
93 |
94 | notifyHidingWindow(context, hwnd);
95 | }
96 | }
97 | return TRUE;
98 | }
99 |
100 | bool applyRules(TRCONTEXT* context)
101 | {
102 | if (!context->autoHiding) {
103 | return false;
104 | }
105 |
106 | return (bool)EnumWindows(EnumWindowsProc, reinterpret_cast(context));
107 | }
108 |
109 | bool clearRules(TRCONTEXT* context)
110 | {
111 | if (context->hidingRules.empty()) {
112 | return false;
113 | }
114 |
115 | for (HIDING_RULE* rule : context->hidingRules) {
116 | delete[] (BYTE*)rule;
117 | }
118 |
119 | context->hidingRules.clear();
120 | return true;
121 | }
122 |
123 | inline static bool compareText(PTCHAR text, PTCHAR pattern, bool isRegex)
124 | {
125 | if (isRegex) {
126 | try {
127 | TREGEX expr(pattern);
128 | return std::regex_match(text, expr);
129 | }
130 | catch (const std::regex_error&) {
131 | return false;
132 | }
133 | }
134 | else {
135 | return _tcscmp(text, pattern) == 0;
136 | }
137 | }
138 |
139 | _Success_(return)
140 | bool matchRule(TRCONTEXT* context, HWND hwnd, bool isMinimizing, _Out_ HIDING_RULE **rulePtr)
141 | {
142 | TCHAR windowText[MAX_WINDOW_TEXT] {};
143 | TCHAR className[MAX_CLASS_NAME] {};
144 | TCHAR exeFileName[MAX_PATH] {};
145 | if (!GetWindowText(hwnd, windowText, MAX_WINDOW_TEXT) ||
146 | !GetClassName(hwnd, className, MAX_CLASS_NAME) ||
147 | !GetWindowExeFileName(hwnd, exeFileName, MAX_PATH)) {
148 |
149 | return false;
150 | }
151 | for (auto rule : context->hidingRules) {
152 | if (isMinimizing) {
153 | if (!(rule->flag & RULE_ON_MINIMIZE)) {
154 | continue;
155 | }
156 | }
157 | else {
158 | if (rule->flag & RULE_AUTO_OFF) {
159 | continue;
160 | }
161 | }
162 | auto text = rule->ruleData;
163 | text += _tcsclen(text) + 1;
164 | if (!compareText(windowText, text, rule->flag & RULE_REGEX_WINDOW_TEXT)) {
165 | continue;
166 | }
167 | text += _tcsclen(text) + 1;
168 | if (!compareText(className, text, rule->flag & RULE_REGEX_WINDOW_CLASS)) {
169 | continue;
170 | }
171 | text += _tcsclen(text) + 1;
172 | if (!compareText(exeFileName, text, rule->flag & RULE_REGEX_EXE_FILENAME)) {
173 | continue;
174 | }
175 | *rulePtr = rule;
176 | return true;
177 | }
178 | return false;
179 | }
180 |
181 | static BOOL CALLBACK DialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
182 | {
183 | RuleEditor* editor;
184 | if (WM_INITDIALOG == message) {
185 | editor = reinterpret_cast(lParam);
186 | editor->initialize(hwndDlg);
187 | return TRUE;
188 | }
189 | editor = reinterpret_cast(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
190 | return editor->dispatchMessage(message, wParam, lParam);
191 | }
192 |
193 | INT_PTR showRulesDlg(HWND parent, TRCONTEXT* context)
194 | {
195 | static bool dialogOpened = false;
196 | if (dialogOpened) {
197 | return FALSE;
198 | }
199 | dialogOpened = true;
200 | auto editor = RuleEditor(context);
201 | auto result = DialogBoxParam(
202 | i18n.lang(context->instance),
203 | MAKEINTRESOURCE(IDD_RULES),
204 | parent,
205 | (DLGPROC)DialogProc,
206 | (LPARAM)&editor
207 | );
208 | dialogOpened = false;
209 | return result;
210 | }
211 |
212 | inline static bool testRegex(HWND hwnd, PTCHAR pattern)
213 | {
214 | TCHAR errMsg[MAX_MSG]{};
215 | try {
216 | TREGEX expr(pattern);
217 | }
218 | catch (const std::regex_error&) {
219 | _stprintf_s(errMsg, i18n[IDS_INVALID_REGEX], pattern);
220 | MessageBox(hwnd, errMsg, APP_NAME, MB_OK | MB_ICONWARNING);
221 | return false;
222 | }
223 | return true;
224 | }
225 |
226 | HIDING_RULE* RuleEditor::newRule()
227 | {
228 | size_t size = sizeof(HIDING_RULE);
229 | TCHAR ruleName[MAX_RULE_NAME],
230 | windowText[MAX_WINDOW_TEXT],
231 | windowClassName[MAX_CLASS_NAME],
232 | exeFileName[MAX_PATH];
233 |
234 | auto ruleNameSize = Edit_GetText(nameEdit, ruleName, MAX_RULE_NAME) + 1,
235 | windowTextSize = Edit_GetText(textEdit, windowText, MAX_WINDOW_TEXT) + 1,
236 | windowClassNameSize = Edit_GetText(classEdit, windowClassName, MAX_CLASS_NAME) + 1,
237 | exeFileNameSize = Edit_GetText(pathEdit, exeFileName, MAX_PATH) + 1;
238 | bool isWindowTextRegex = Button_GetCheck(textCheckBox) == BST_CHECKED,
239 | isWindowClassNameRegex = Button_GetCheck(classCheckBox) == BST_CHECKED,
240 | isExeFileNameRegex = Button_GetCheck(pathCheckBox) == BST_CHECKED,
241 | isAlwaysNotify = Button_GetCheck(alwaysNotifyRadioBox) == BST_CHECKED,
242 | isNotifyFirstTime = Button_GetCheck(notifyFirstTimeRadioBox) == BST_CHECKED,
243 | isOnMinimize = Button_GetCheck(onMinimizeRadioBox) == BST_CHECKED,
244 | isOnBoth = Button_GetCheck(onBothRadioBox) == BST_CHECKED;
245 |
246 | if (ruleNameSize == 1 || windowTextSize == 1 || windowClassNameSize == 1 || exeFileNameSize == 1) {
247 | MessageBox(window, i18n[IDS_RULE_INFO_REQUIRED], APP_NAME, MB_OK | MB_ICONWARNING);
248 | return NULL;
249 | }
250 |
251 | if (isWindowTextRegex && !testRegex(window, windowText)) {
252 | return NULL;
253 | }
254 | if (isWindowClassNameRegex && !testRegex(window, windowClassName)) {
255 | return NULL;
256 | }
257 | if (isExeFileNameRegex && !testRegex(window, exeFileName)) {
258 | return NULL;
259 | }
260 |
261 | size += (ruleNameSize + windowTextSize + windowClassNameSize + exeFileNameSize) * sizeof(TCHAR);
262 | HIDING_RULE* rule = (HIDING_RULE*)new BYTE[size];
263 | rule->size = size;
264 | rule->flag = 0;
265 | if (isWindowTextRegex) rule->flag |= RULE_REGEX_WINDOW_TEXT;
266 | if (isWindowClassNameRegex) rule->flag |= RULE_REGEX_WINDOW_CLASS;
267 | if (isExeFileNameRegex) rule->flag |= RULE_REGEX_EXE_FILENAME;
268 | if (isOnMinimize) {
269 | rule->flag |= (RULE_ON_MINIMIZE | RULE_AUTO_OFF);
270 | }
271 | else if (isOnBoth) {
272 | rule->flag |= RULE_ON_MINIMIZE;
273 | }
274 | if (isAlwaysNotify) {
275 | rule->flag |= RULE_ALWAYS_NOTIFY;
276 | }
277 | else if (isNotifyFirstTime) {
278 | rule->flag |= RULE_NOTIFY_FIRST_TIME;
279 | }
280 | PTCHAR ruleDataOffset = rule->ruleData;
281 | memcpy(ruleDataOffset, ruleName, ruleNameSize * sizeof(TCHAR));
282 | ruleDataOffset += ruleNameSize;
283 | memcpy(ruleDataOffset, windowText, windowTextSize * sizeof(TCHAR));
284 | ruleDataOffset += windowTextSize;
285 | memcpy(ruleDataOffset, windowClassName, windowClassNameSize * sizeof(TCHAR));
286 | ruleDataOffset += windowClassNameSize;
287 | memcpy(ruleDataOffset, exeFileName, exeFileNameSize * sizeof(TCHAR));
288 | return rule;
289 | }
290 |
291 | RuleEditor::RuleEditor(TRCONTEXT* context)
292 | {
293 | this->context = context;
294 | }
295 |
296 | TRCONTEXT* RuleEditor::getContext()
297 | {
298 | return context;
299 | }
300 |
301 | void RuleEditor::initialize(HWND hwnd)
302 | {
303 | SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(this));
304 | window = hwnd;
305 | ruleList = GetDlgItem(hwnd, IDC_LIST_RULES);
306 | nameEdit = GetDlgItem(hwnd, IDC_EDIT_NAME);
307 | textEdit = GetDlgItem(hwnd, IDC_EDIT_TEXT);
308 | classEdit = GetDlgItem(hwnd, IDC_EDIT_CLASS);
309 | pathEdit = GetDlgItem(hwnd, IDC_EDIT_PATH);
310 | textCheckBox = GetDlgItem(hwnd, IDC_CHECK_REGEX_TEXT);
311 | classCheckBox = GetDlgItem(hwnd, IDC_CHECK_REGEX_CLASS);
312 | pathCheckBox = GetDlgItem(hwnd, IDC_CHECK_REGEX_PATH);
313 | saveButton = GetDlgItem(hwnd, IDC_SAVE);
314 | removeButton = GetDlgItem(hwnd, IDC_REMOVE);
315 | dropButton = GetDlgItem(hwnd, IDABORT);
316 | windowsCombo = GetDlgItem(hwnd, IDC_COMBO_WINDOWS);
317 | onMinimizeRadioBox = GetDlgItem(hwnd, IDC_RADIO_ON_MINIMIZE);
318 | onFirstShowRadioBox = GetDlgItem(hwnd, IDC_RADIO_ON_FIRST_SHOW);
319 | onBothRadioBox = GetDlgItem(hwnd, IDC_RADIO_ON_BOTH);
320 | neverNotifyRadioBox = GetDlgItem(hwnd, IDC_RADIO_NEVER_NOTIFY);
321 | alwaysNotifyRadioBox = GetDlgItem(hwnd, IDC_RADIO_ALWAYS_NOTIFY);
322 | notifyFirstTimeRadioBox = GetDlgItem(hwnd, IDC_RADIO_NOTIFY_FIRST_TIME);
323 | Edit_LimitText(nameEdit, MAX_RULE_NAME);
324 | Edit_LimitText(textEdit, MAX_WINDOW_TEXT);
325 | Edit_LimitText(classEdit, MAX_CLASS_NAME);
326 | Edit_LimitText(pathEdit, MAX_PATH);
327 | sync();
328 | }
329 |
330 | #pragma warning(push)
331 | #pragma warning(disable:4100)
332 | bool RuleEditor::dispatchMessage(UINT message, WPARAM wParam, LPARAM lParam)
333 | #pragma warning(pop)
334 | {
335 | switch (message) {
336 | case WM_COMMAND:
337 | switch (LOWORD(wParam)) {
338 | case IDHELP:
339 | ShellExecute(window, _T("open"), HELP_URL, NULL, NULL, SW_SHOWNORMAL);
340 | break;
341 | case IDABORT:
342 | drop();
343 | return TRUE;
344 | case IDCANCEL:
345 | return EndDialog(window, wParam);
346 | case IDC_NEW:
347 | return append();
348 | case IDC_REMOVE:
349 | return remove();
350 | case IDC_SAVE:
351 | return save();
352 | case IDC_LIST_RULES:
353 | switch (HIWORD(wParam)) {
354 | case LBN_SELCANCEL:
355 | case LBN_SELCHANGE:
356 | select();
357 | break;
358 | }
359 | break;
360 | case IDC_EDIT_NAME:
361 | case IDC_EDIT_TEXT:
362 | case IDC_EDIT_CLASS:
363 | case IDC_EDIT_PATH:
364 | if (HIWORD(wParam) == EN_CHANGE) {
365 | touch();
366 | }
367 | break;
368 | case IDC_RADIO_ON_MINIMIZE:
369 | case IDC_RADIO_ON_FIRST_SHOW:
370 | case IDC_RADIO_ON_BOTH:
371 | case IDC_RADIO_NEVER_NOTIFY:
372 | case IDC_RADIO_ALWAYS_NOTIFY:
373 | case IDC_RADIO_NOTIFY_FIRST_TIME:
374 | case IDC_CHECK_REGEX_CLASS:
375 | case IDC_CHECK_REGEX_PATH:
376 | case IDC_CHECK_REGEX_TEXT:
377 | touch();
378 | break;
379 | case IDC_COMBO_WINDOWS:
380 | if (HIWORD(wParam) == CBN_SELCHANGE) {
381 | fill();
382 | }
383 | break;
384 | }
385 | break;
386 | }
387 | return FALSE;
388 | }
389 |
390 | bool RuleEditor::append()
391 | {
392 | if (dirty()) {
393 | switch (MessageBox(window, i18n[IDS_UNSAVED], APP_NAME, MB_YESNOCANCEL | MB_ICONQUESTION)) {
394 | case IDYES:
395 | save();
396 | break;
397 | case IDNO:
398 | drop();
399 | break;
400 | case IDCANCEL:
401 | return FALSE;
402 | }
403 | }
404 | sync();
405 | TCHAR ruleName[MAX_RULE_NAME];
406 | _stprintf_s(ruleName, _T("* %s"), i18n[IDS_NEW_RULE]);
407 | auto index = ListBox_AddString(ruleList, ruleName);
408 | ListBox_SetCurSel(ruleList, index);
409 | reset(true);
410 | select();
411 | enable();
412 | Edit_SetText(nameEdit, i18n[IDS_NEW_RULE]);
413 | return TRUE;
414 | }
415 |
416 | bool RuleEditor::remove()
417 | {
418 | auto index = ListBox_GetCurSel(ruleList);
419 | if (index < 0) {
420 | return false;
421 | }
422 | if ((size_t)index < context->hidingRules.size()) {
423 | auto rule = context->hidingRules[index];
424 | delete[] (BYTE*)rule;
425 | VECTOR_ERASE(context->hidingRules, index);
426 | }
427 | ListBox_DeleteString(ruleList, index);
428 | ListBox_SetCurSel(ruleList, -1);
429 | select();
430 | enable(false);
431 | reset(false);
432 | clean();
433 | return saveRules(context);
434 | }
435 |
436 | void RuleEditor::reset(bool init)
437 | {
438 | Button_SetCheck(textCheckBox, BST_UNCHECKED);
439 | Button_SetCheck(classCheckBox, BST_UNCHECKED);
440 | Button_SetCheck(pathCheckBox, BST_UNCHECKED);
441 | Button_SetCheck(onMinimizeRadioBox, BST_UNCHECKED);
442 | Button_SetCheck(onBothRadioBox, BST_UNCHECKED);
443 | Button_SetCheck(alwaysNotifyRadioBox, BST_UNCHECKED);
444 | Button_SetCheck(notifyFirstTimeRadioBox, BST_UNCHECKED);
445 | Edit_SetText(nameEdit, NULL);
446 | Edit_SetText(textEdit, NULL);
447 | Edit_SetText(classEdit, NULL);
448 | Edit_SetText(pathEdit, NULL);
449 | if (init) {
450 | Button_SetCheck(onFirstShowRadioBox, BST_CHECKED);
451 | Button_SetCheck(neverNotifyRadioBox, BST_CHECKED);
452 | }
453 | else {
454 | Button_SetCheck(onFirstShowRadioBox, BST_UNCHECKED);
455 | Button_SetCheck(neverNotifyRadioBox, BST_UNCHECKED);
456 | }
457 | }
458 |
459 | void RuleEditor::enable(bool val)
460 | {
461 | Edit_Enable(nameEdit, val);
462 | Edit_Enable(textEdit, val);
463 | Edit_Enable(classEdit, val);
464 | Edit_Enable(pathEdit, val);
465 | Button_Enable(textCheckBox, val);
466 | Button_Enable(classCheckBox, val);
467 | Button_Enable(pathCheckBox, val);
468 | ComboBox_Enable(windowsCombo, val);
469 | Button_Enable(onMinimizeRadioBox, val);
470 | Button_Enable(onFirstShowRadioBox, val);
471 | Button_Enable(onBothRadioBox, val);
472 | Button_Enable(neverNotifyRadioBox, val);
473 | Button_Enable(alwaysNotifyRadioBox, val);
474 | Button_Enable(notifyFirstTimeRadioBox, val);
475 | }
476 |
477 | void RuleEditor::touch()
478 | {
479 | if (!isDirty && !isBusy) {
480 | isDirty = true;
481 | Button_Enable(saveButton, TRUE);
482 | Button_Enable(dropButton, TRUE);
483 | ListBox_Enable(ruleList, FALSE);
484 | }
485 | }
486 |
487 | void RuleEditor::clean()
488 | {
489 | if (isDirty) {
490 | isDirty = false;
491 | Button_Enable(saveButton, FALSE);
492 | Button_Enable(dropButton, FALSE);
493 | ListBox_Enable(ruleList, TRUE);
494 | }
495 | }
496 |
497 | void RuleEditor::sync()
498 | {
499 | ListBox_ResetContent(ruleList);
500 | for (HIDING_RULE* rule : context->hidingRules) {
501 | ListBox_AddString(ruleList, rule->ruleData);
502 | }
503 |
504 | ComboBox_ResetContent(windowsCombo);
505 | TCHAR windowText[MAX_WINDOW_TEXT]{};
506 | for (int i = 0; i < context->iconIndex; i++) {
507 | auto hiddenWindow = context->icons[i].window;
508 | if (GetWindowText(hiddenWindow, windowText, MAX_WINDOW_TEXT) == 0) {
509 | continue;
510 | }
511 | ComboBox_SetItemData(windowsCombo, ComboBox_AddString(windowsCombo, windowText), i);
512 | }
513 | }
514 |
515 | void RuleEditor::drop()
516 | {
517 | auto index = ListBox_GetCurSel(ruleList);
518 | if ((size_t)index >= context->hidingRules.size()) {
519 | ListBox_DeleteString(ruleList, index);
520 | }
521 | ListBox_SetCurSel(ruleList, -1);
522 | select();
523 | enable(false);
524 | reset(false);
525 | clean();
526 | }
527 |
528 | void RuleEditor::select()
529 | {
530 | isBusy = true;
531 | auto index = ListBox_GetCurSel(ruleList);
532 | Button_Enable(removeButton, index > -1 ? TRUE : FALSE);
533 | if (index > -1 && (size_t)index < context->hidingRules.size()) {
534 | enable();
535 | auto rule = context->hidingRules[index];
536 | auto text = rule->ruleData;
537 | Edit_SetText(nameEdit, text);
538 | text += _tcsclen(text) + 1;
539 | Edit_SetText(textEdit, text);
540 | text += _tcsclen(text) + 1;
541 | Edit_SetText(classEdit, text);
542 | text += _tcsclen(text) + 1;
543 | Edit_SetText(pathEdit, text);
544 | Button_SetCheck(textCheckBox, rule->flag & RULE_REGEX_WINDOW_TEXT ? BST_CHECKED : BST_UNCHECKED);
545 | Button_SetCheck(classCheckBox, rule->flag & RULE_REGEX_WINDOW_CLASS ? BST_CHECKED : BST_UNCHECKED);
546 | Button_SetCheck(pathCheckBox, rule->flag & RULE_REGEX_EXE_FILENAME ? BST_CHECKED : BST_UNCHECKED);
547 |
548 | int onMinimizeChecked = BST_UNCHECKED,
549 | onFirstShowChecked = BST_UNCHECKED,
550 | onBothChecked = BST_UNCHECKED;
551 | if (rule->flag & RULE_ON_MINIMIZE) {
552 | if (rule->flag & RULE_AUTO_OFF) {
553 | onMinimizeChecked = BST_CHECKED;
554 | }
555 | else {
556 | onBothChecked = BST_CHECKED;
557 | }
558 | }
559 | else {
560 | onFirstShowChecked = BST_CHECKED;
561 | }
562 | Button_SetCheck(onMinimizeRadioBox, onMinimizeChecked);
563 | Button_SetCheck(onFirstShowRadioBox, onFirstShowChecked);
564 | Button_SetCheck(onBothRadioBox, onBothChecked);
565 |
566 | int neverNotifyChecked = BST_UNCHECKED,
567 | alwaysNotifyChecked = BST_UNCHECKED,
568 | notifyFirstTimeChecked = BST_UNCHECKED;
569 | if (rule->flag & RULE_ALWAYS_NOTIFY) {
570 | alwaysNotifyChecked = true;
571 | } else if (rule->flag & RULE_NOTIFY_FIRST_TIME) {
572 | notifyFirstTimeChecked = true;
573 | }
574 | else {
575 | neverNotifyChecked = true;
576 | }
577 | Button_SetCheck(neverNotifyRadioBox, neverNotifyChecked);
578 | Button_SetCheck(alwaysNotifyRadioBox, alwaysNotifyChecked);
579 | Button_SetCheck(notifyFirstTimeRadioBox, notifyFirstTimeChecked);
580 | }
581 | isBusy = false;
582 | }
583 |
584 | void RuleEditor::fill()
585 | {
586 | Button_SetCheck(textCheckBox, BST_UNCHECKED);
587 | Button_SetCheck(classCheckBox, BST_UNCHECKED);
588 | Button_SetCheck(pathCheckBox, BST_UNCHECKED);
589 |
590 | auto index = ComboBox_GetItemData(windowsCombo, ComboBox_GetCurSel(windowsCombo));
591 | if (index >= context->iconIndex) {
592 | return;
593 | }
594 |
595 | auto hwnd = context->icons[index].window;
596 | TCHAR windowText[MAX_WINDOW_TEXT]{};
597 | TCHAR className[MAX_CLASS_NAME]{};
598 | TCHAR exeFileName[MAX_PATH]{};
599 | if (GetWindowText(hwnd, windowText, MAX_WINDOW_TEXT)) {
600 | Edit_SetText(textEdit, windowText);
601 | }
602 | if (GetClassName(hwnd, className, MAX_CLASS_NAME)) {
603 | Edit_SetText(classEdit, className);
604 | }
605 | if (GetWindowExeFileName(hwnd, exeFileName, MAX_PATH)) {
606 | Edit_SetText(pathEdit, exeFileName);
607 | }
608 | }
609 |
610 | bool RuleEditor::dirty()
611 | {
612 | return isDirty;
613 | }
614 |
615 | bool RuleEditor::save()
616 | {
617 | auto index = ListBox_GetCurSel(ruleList);
618 | HIDING_RULE* rule = newRule();
619 | if (NULL == rule) {
620 | return false;
621 | }
622 | if ((size_t)index >= context->hidingRules.size()) {
623 | context->hidingRules.push_back(rule);
624 | }
625 | else {
626 | auto originalRule = context->hidingRules[index];
627 | delete[] (BYTE*)originalRule;
628 | context->hidingRules[index] = rule;
629 | }
630 | ListBox_DeleteString(ruleList, index);
631 | ListBox_InsertString(ruleList, index, rule->ruleData);
632 | ListBox_SetCurSel(ruleList, index);
633 | select();
634 | clean();
635 | return saveRules(context);
636 | }
637 |
--------------------------------------------------------------------------------
/src/rules.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef UNICODE
4 | #define TREGEX std::wregex
5 | #else
6 | #define TREGEX std::regex
7 | #endif
8 |
9 | bool loadRules(TRCONTEXT* context);
10 | bool applyRules(TRCONTEXT* context);
11 | bool saveRules(TRCONTEXT* context);
12 | bool clearRules(TRCONTEXT* context);
13 | _Success_(return)
14 | bool matchRule(TRCONTEXT* context, HWND hwnd, bool isMinimizing, _Out_ HIDING_RULE **rulePtr);
15 | INT_PTR showRulesDlg(HWND parent, TRCONTEXT* context);
16 |
17 | class RuleEditor final {
18 | private:
19 | TRCONTEXT* context;
20 | HWND window = NULL;
21 | HWND ruleList = NULL;
22 | HWND nameEdit = NULL;
23 | HWND textEdit = NULL;
24 | HWND classEdit = NULL;
25 | HWND pathEdit = NULL;
26 | HWND textCheckBox = NULL;
27 | HWND classCheckBox = NULL;
28 | HWND pathCheckBox = NULL;
29 | HWND saveButton = NULL;
30 | HWND removeButton = NULL;
31 | HWND dropButton = NULL;
32 | HWND windowsCombo = NULL;
33 | HWND onMinimizeRadioBox = NULL;
34 | HWND onFirstShowRadioBox = NULL;
35 | HWND onBothRadioBox = NULL;
36 | HWND neverNotifyRadioBox = NULL;
37 | HWND alwaysNotifyRadioBox = NULL;
38 | HWND notifyFirstTimeRadioBox = NULL;
39 | int ruleId = -1;
40 | bool isDirty = false;
41 | bool isBusy = false;
42 | private:
43 | HIDING_RULE* newRule();
44 | public:
45 | RuleEditor(TRCONTEXT* context);
46 | void initialize(HWND hwnd);
47 | void enable(bool val = true);
48 | void touch();
49 | void clean();
50 | void sync();
51 | void drop();
52 | void select();
53 | void fill();
54 | void reset(bool init = true);
55 | bool dirty();
56 | bool save();
57 | bool append();
58 | bool remove();
59 | bool dispatchMessage(UINT message, WPARAM wParam, LPARAM lParam);
60 | TRCONTEXT* getContext();
61 | };
62 |
--------------------------------------------------------------------------------
/src/traymond.cpp:
--------------------------------------------------------------------------------
1 | #define TRAYMON_MAIN
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "traymond.h"
10 | #include "options.h"
11 | #include "winevent.h"
12 | #include "rules.h"
13 | #include "logging.h"
14 | #include "icons.h"
15 |
16 | HANDLE saveFile;
17 | TRCONTEXT appContext = {};
18 |
19 |
20 | // Saves our hidden windows so they can be restored in case
21 | // of crashing.
22 | static void save(const TRCONTEXT *context) {
23 | DWORD numbytes;
24 | // Truncate file
25 | SetFilePointer(saveFile, 0, NULL, FILE_BEGIN);
26 | SetEndOfFile(saveFile);
27 | if (!context->iconIndex) {
28 | return;
29 | }
30 | for (int i = 0; i < context->iconIndex; i++) {
31 | if (context->icons[i].window) {
32 | std::string str;
33 | str = std::to_string((long)context->icons[i].window);
34 | str += ',';
35 | const char *handleString = str.c_str();
36 | WriteFile(saveFile, handleString, strlen(handleString), &numbytes, NULL);
37 | }
38 | }
39 | }
40 |
41 | // Creates our tray icon menu
42 | static HMENU createTrayMenu(const TRCONTEXT* context) {
43 | HMENU popupMenu = GetSubMenu(LoadMenu(i18n.lang(context->instance), MAKEINTRESOURCE(IDM_POPUP)), 0);
44 | MENUINFO mi{};
45 | mi.cbSize = sizeof(MENUINFO);
46 | mi.fMask = MIM_STYLE;
47 | GetMenuInfo(popupMenu, &mi);
48 | mi.dwStyle |= MNS_NOTIFYBYPOS;
49 | SetMenuInfo(popupMenu, &mi);
50 | SetMenuDefaultItem(popupMenu, IDM_OPTIONS, FALSE);
51 | return popupMenu;
52 | }
53 |
54 | // Remove menu item from tray popup menu
55 | static void removeMenuItem(TRCONTEXT* context, int index) {
56 | DestroyMenu(context->trayMenu);
57 | context->trayMenu = createTrayMenu(context);
58 |
59 | for (int i = 0; i < context->iconIndex; i++) {
60 | auto hw = context->icons + i;
61 | if (hw->hideType != HideMenu) {
62 | continue;
63 | }
64 | if (i == index) {
65 | DeleteObject(hw->menu.info.hbmpItem);
66 | continue;
67 | }
68 | hw->menu.info.dwTypeData = hw->menu.caption;
69 | InsertMenuItem(context->trayMenu, 0, TRUE, &hw->menu.info);
70 | }
71 | }
72 |
73 | HICON getWindowIcon(const TRCONTEXT* context, HWND hwnd) {
74 | HICON icon = (HICON)SendMessage(hwnd, WM_GETICON, ICON_SMALL2, NULL);
75 | if (!icon) {
76 | icon = (HICON)GetClassLongPtr(hwnd, GCLP_HICONSM);
77 | if (!icon) {
78 | return context->mainIcon;
79 | }
80 | }
81 | return icon;
82 | }
83 |
84 | static HBITMAP IconToBitmap(HICON icon, int width = 0, int height = 0) {
85 | if (!width) width = GetSystemMetrics(SM_CXSMICON);
86 | if (!height) height = GetSystemMetrics(SM_CYSMICON);
87 | HDC hdc = GetDC(NULL);
88 | HDC hdcMem = CreateCompatibleDC(hdc);
89 | BITMAPINFO bmi;
90 | memset(&bmi, 0, sizeof(BITMAPINFO));
91 | bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
92 | bmi.bmiHeader.biWidth = width;
93 | bmi.bmiHeader.biHeight = height;
94 | bmi.bmiHeader.biPlanes = 1;
95 | bmi.bmiHeader.biBitCount = 32;
96 | bmi.bmiHeader.biCompression = BI_RGB;
97 | #pragma warning(push)
98 | #pragma warning(disable:6387)
99 | HBITMAP hbmpMem = CreateDIBSection(hdcMem, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
100 | #pragma warning(pop)
101 | if (hbmpMem) {
102 | auto oldObj = SelectObject(hdcMem, hbmpMem);
103 | DrawIconEx(hdcMem, 0, 0, icon, width, height, 0, NULL, DI_NORMAL);
104 | SelectObject(hdcMem, oldObj);
105 | }
106 | DeleteDC(hdcMem);
107 | ReleaseDC(NULL, hdc);
108 | return hbmpMem;
109 | }
110 |
111 | // Revise hidden window icon hide type
112 | // Return the number of icons moved
113 | int reviseHiddenWindowIcon(TRCONTEXT* context) {
114 | int count = 0;
115 | HIDDEN_WINDOW hiddenWindow;
116 | for (int i = 0; i < context->iconIndex; i++) {
117 | auto hideType = context->icons[i].hideType;
118 | if (hideType == context->hideType) {
119 | continue;
120 | }
121 | memset(&hiddenWindow, 0, sizeof(HIDDEN_WINDOW));
122 | auto currWin = hiddenWindow.window = context->icons[i].window;
123 | switch (hideType) {
124 | case HideMenu:
125 | removeMenuItem(context, i);
126 | switch (context->hideType) {
127 | case HideTray:
128 | hiddenWindow.hideType = HideTray;
129 | auto nid = &hiddenWindow.icon;
130 | nid->cbSize = sizeof(NOTIFYICONDATA);
131 | nid->hWnd = context->mainWindow;
132 | nid->hIcon = getWindowIcon(context, currWin);
133 | nid->uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
134 | nid->uVersion = NOTIFYICON_VERSION;
135 | nid->uID = reinterpret_cast(currWin);
136 | nid->uCallbackMessage = WM_ICON;
137 | GetWindowText(currWin, hiddenWindow.icon.szTip, MAX_WINDOW_TEXT);
138 | Shell_NotifyIcon(NIM_ADD, &hiddenWindow.icon);
139 | Shell_NotifyIcon(NIM_SETVERSION, &hiddenWindow.icon);
140 | context->icons[i] = hiddenWindow;
141 | count++;
142 | break;
143 | }
144 | break;
145 | case HideTray:
146 | Shell_NotifyIcon(NIM_DELETE, &context->icons[i].icon);
147 | switch (context->hideType) {
148 | case HideMenu:
149 | auto mid = &hiddenWindow.menu;
150 | auto mii = &mid->info;
151 | hiddenWindow.hideType = HideMenu;
152 | mii->hbmpItem = IconToBitmap(getWindowIcon(context, currWin));
153 | mii->cbSize = sizeof(MENUITEMINFO);
154 | mii->fMask = MIIM_STRING | MIIM_ID | MIIM_BITMAP | MIIM_DATA;
155 | mii->fType = MFT_STRING | MFT_BITMAP;
156 | mii->dwTypeData = mid->caption;
157 | mii->cch = GetWindowText(currWin, mii->dwTypeData, MAX_WINDOW_TEXT);
158 | mii->wID = 0;
159 | mii->dwItemData = reinterpret_cast(currWin);
160 | InsertMenuItem(context->trayMenu, 0, TRUE, mii);
161 | context->icons[i] = hiddenWindow;
162 | count++;
163 | break;
164 | }
165 | break;
166 | }
167 | }
168 | save(context); // Maybe not necessary
169 | return count;
170 | }
171 |
172 | // Restores a window
173 | bool restoreWindow(TRCONTEXT *context, UINT xID, HWND hwnd) {
174 | for (int i = 0; i < context->iconIndex; i++) {
175 | auto icon = context->icons + i;
176 | switch (icon->hideType) {
177 | case HideTray:
178 | if (icon->icon.uID != xID && icon->window != hwnd) continue;
179 | Shell_NotifyIcon(NIM_DELETE, &icon->icon);
180 | break;
181 | case HideMenu:
182 | if (icon->menu.info.dwItemData != xID && icon->window != hwnd) continue;
183 | removeMenuItem(context, i);
184 | break;
185 | default:
186 | continue;
187 | }
188 |
189 | auto currWin = icon->window;
190 | if (IsWindow(currWin)) {
191 | ShowWindow(currWin, SW_NORMAL);
192 | SetForegroundWindow(currWin);
193 | }
194 |
195 | *icon = {};
196 | std::vector temp = std::vector(context->iconIndex);
197 | // Restructure array so there are no holes
198 | for (int j = 0, x = 0; j < context->iconIndex; j++) {
199 | if (context->icons[j].window) {
200 | temp[x] = context->icons[j];
201 | x++;
202 | }
203 | }
204 | memcpy_s(context->icons, sizeof(context->icons), &temp.front(), sizeof(HIDDEN_WINDOW)*context->iconIndex);
205 | context->iconIndex--;
206 | save(context);
207 | context->hiddenWindows.erase(currWin);
208 | context->freeWindows.insert(currWin);
209 | return true;
210 | }
211 | return false;
212 | }
213 |
214 | // Minimizes the current window to tray or menu.
215 | // Uses currently focused window unless supplied a handle as the argument.
216 | bool minimizeWindow(TRCONTEXT *context, HWND currWin, bool restored) {
217 | // Taskbar and desktop windows are restricted from hiding.
218 | const TCHAR restrictWins[][14] = { {_T("WorkerW")}, {_T("Shell_TrayWnd")} };
219 |
220 | if (!currWin) {
221 | currWin = GetForegroundWindow();
222 | }
223 |
224 | DWORD processId = 0;
225 | GetWindowThreadProcessId(currWin, &processId);
226 | if (!currWin || !isTopLevelWindow(currWin) || GetCurrentProcessId() == processId) {
227 | return false;
228 | }
229 |
230 | for (int i = 0; i < context->iconIndex; i++) {
231 | if (currWin == context->icons[i].window) {
232 | return IsWindowVisible(currWin) && !ShowWindow(currWin, SW_HIDE);
233 | }
234 | }
235 |
236 | TCHAR className[MAX_CLASS_NAME];
237 | if (!GetClassName(currWin, className, MAX_CLASS_NAME)) {
238 | return false;
239 | }
240 | for (int i = 0; i < sizeof(restrictWins) / sizeof(*restrictWins); i++) {
241 | if (_tcscmp(restrictWins[i], className) == 0) {
242 | return false;
243 | }
244 | }
245 |
246 | if (context->iconIndex == MAXIMUM_WINDOWS) {
247 | MessageBox(NULL, i18n[IDS_TOO_MANY_HIDDEN_WINDOWS], APP_NAME, MB_OK | MB_ICONERROR);
248 | return false;
249 | }
250 |
251 | if (IsWindowVisible(currWin) && !ShowWindow(currWin, SW_HIDE)) {
252 | MessageBeep(MB_ICONWARNING);
253 | return false;
254 | }
255 | switch (context->icons[context->iconIndex].hideType = context->hideType) {
256 | case HideTray:
257 | NOTIFYICONDATA nid;
258 | nid.cbSize = sizeof(NOTIFYICONDATA);
259 | nid.hWnd = context->mainWindow;
260 | nid.hIcon = getWindowIcon(context, currWin);
261 | nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
262 | nid.uVersion = NOTIFYICON_VERSION;
263 | nid.uID = reinterpret_cast(currWin);
264 | nid.uCallbackMessage = WM_ICON;
265 | GetWindowText(currWin, nid.szTip, MAX_WINDOW_TEXT);
266 | context->icons[context->iconIndex].icon = nid;
267 | Shell_NotifyIcon(NIM_ADD, &nid);
268 | Shell_NotifyIcon(NIM_SETVERSION, &nid);
269 | break;
270 | case HideMenu:
271 | MENUITEMINFO mii;
272 | mii.hbmpItem = IconToBitmap(getWindowIcon(context, currWin));
273 | mii.cbSize = sizeof(MENUITEMINFO);
274 | mii.fMask = MIIM_STRING | MIIM_ID | MIIM_BITMAP | MIIM_DATA;
275 | mii.fType = MFT_STRING | MFT_BITMAP;
276 | mii.dwTypeData = context->icons[context->iconIndex].menu.caption;
277 | mii.cch = GetWindowText(currWin, mii.dwTypeData, MAX_WINDOW_TEXT);
278 | mii.wID = 0;
279 | mii.dwItemData = reinterpret_cast(currWin);
280 | context->icons[context->iconIndex].menu.info = mii;
281 | InsertMenuItem(context->trayMenu, 0, TRUE, &mii);
282 | break;
283 | }
284 | context->icons[context->iconIndex].window = currWin;
285 | context->iconIndex++;
286 | if (!restored) {
287 | save(context);
288 | }
289 | context->hiddenWindows.insert(currWin);
290 | context->freeWindows.erase(currWin);
291 | return true;
292 | }
293 |
294 | // Adds our own icon to tray
295 | static void createTrayIcon(HWND mainWindow, NOTIFYICONDATA* icon) {
296 | icon->cbSize = sizeof(NOTIFYICONDATA);
297 | icon->hWnd = mainWindow;
298 | icon->uFlags = NIF_ICON | NIF_TIP | NIF_SHOWTIP | NIF_MESSAGE;
299 | icon->uVersion = NOTIFYICON_VERSION;
300 | icon->uID = reinterpret_cast(mainWindow);
301 | icon->uCallbackMessage = WM_OURICON;
302 | _tcscpy_s(icon->szTip, APP_NAME);
303 | Shell_NotifyIcon(NIM_ADD, icon);
304 | Shell_NotifyIcon(NIM_SETVERSION, icon);
305 | }
306 |
307 | BOOL notifyHidingWindow(TRCONTEXT* context, HWND hwnd)
308 | {
309 | TCHAR message[MAX_MSG]{ NULL };
310 | TCHAR windowText[MAX_WINDOW_TEXT]{ NULL };
311 | TCHAR exeFileName[MAX_PATH]{ NULL };
312 | GetWindowText(hwnd, windowText, MAX_WINDOW_TEXT);
313 | GetWindowExeFileName(hwnd, exeFileName, MAX_PATH);
314 | _stprintf_s(message, _T("%s:\n%s"), windowText, exeFileName);
315 | auto mainWindow = context->mainWindow;
316 | NOTIFYICONDATA icon{};
317 | icon.cbSize = sizeof(icon);
318 | icon.hWnd = mainWindow;
319 | icon.uFlags = NIF_INFO;
320 | icon.uVersion = NOTIFYICON_VERSION;
321 | icon.uID = reinterpret_cast(mainWindow);
322 | _tcscpy_s(icon.szInfo, message);
323 | _tcscpy_s(icon.szInfoTitle, i18n[IDS_HIDING_WINDOW]);
324 | return Shell_NotifyIcon(NIM_MODIFY, &icon);
325 | }
326 |
327 | // Shows all hidden windows;
328 | static void showAllWindows(TRCONTEXT *context) {
329 | context->hiddenWindows.clear();
330 | for (int i = 0; i < context->iconIndex; i++)
331 | {
332 | auto currWin = context->icons[i].window;
333 | ShowWindow(context->icons[i].window, SW_NORMAL);
334 | context->freeWindows.insert(currWin);
335 | switch (context->icons[i].hideType) {
336 | case HideTray: Shell_NotifyIcon(NIM_DELETE, &context->icons[i].icon); break;
337 | case HideMenu: removeMenuItem(context, i); break;
338 | }
339 | context->icons[i] = {};
340 | }
341 | save(context);
342 | context->iconIndex = 0;
343 | }
344 |
345 | static void exitApp() {
346 | PostQuitMessage(0);
347 | }
348 |
349 | static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
350 | if (hwnd == reinterpret_cast(lParam)) {
351 | SetLastError(TOP_LEVEL_WINDOW_ERROR);
352 | return FALSE;
353 | }
354 | return TRUE;
355 | }
356 |
357 | static BOOL WINAPI _IsTopLevelWindow(HWND hwnd) {
358 | if (!EnumWindows(EnumWindowsProc, reinterpret_cast(hwnd)) && GetLastError() == TOP_LEVEL_WINDOW_ERROR) {
359 | return TRUE;
360 | }
361 | return FALSE;
362 | }
363 |
364 | static void setupIsTopLevelWindow()
365 | {
366 | auto user32Module = GetModuleHandle(_T("user32.dll"));
367 | if (user32Module) {
368 | isTopLevelWindow = (IsTopLevelWindow)GetProcAddress(user32Module, "IsTopLevelWindow");
369 | }
370 | if (NULL == isTopLevelWindow) {
371 | isTopLevelWindow = _IsTopLevelWindow;
372 | }
373 | }
374 |
375 | // Creates and reads the save file to restore hidden windows in case of unexpected termination
376 | static void startup(TRCONTEXT *context) {
377 | setupIsTopLevelWindow();
378 | loadRules(context);
379 | applyRules(context);
380 | hookWinEvent(context);
381 | TCHAR currDir[MAX_PATH] = { NULL };
382 | auto currDirLen = GetModuleFileName(NULL, currDir, MAX_PATH);
383 | for (int i = currDirLen; i > 0; i--) {
384 | if (currDir[i] == '\\') {
385 | currDir[i] = NULL;
386 | break;
387 | }
388 | }
389 | SetCurrentDirectory(currDir);
390 | if ((saveFile = CreateFile(SAVE_FILE_NAME, GENERIC_READ | GENERIC_WRITE, \
391 | 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
392 | MessageBox(NULL, i18n[IDS_SAVE_FILE_ERROR], APP_NAME, MB_OK | MB_ICONERROR);
393 | exitApp();
394 | }
395 | // Check if we've crashed (i. e. there is a save file) during current uptime and
396 | // if there are windows to restore, in which case restore them and
397 | // display a reassuring message.
398 | if (GetLastError() == ERROR_ALREADY_EXISTS) {
399 | DWORD numbytes;
400 | DWORD fileSize = GetFileSize(saveFile, NULL);
401 |
402 | if (!fileSize) {
403 | return;
404 | };
405 |
406 | FILETIME saveFileWriteTime;
407 | GetFileTime(saveFile, NULL, NULL, &saveFileWriteTime);
408 | uint64_t writeTime = ((uint64_t)saveFileWriteTime.dwHighDateTime << 32 | (uint64_t)saveFileWriteTime.dwLowDateTime) / 10000;
409 | GetSystemTimeAsFileTime(&saveFileWriteTime);
410 | writeTime = (((uint64_t)saveFileWriteTime.dwHighDateTime << 32 | (uint64_t)saveFileWriteTime.dwLowDateTime) / 10000) - writeTime;
411 |
412 | if (GetTickCount64() < writeTime) {
413 | return;
414 | }
415 |
416 | std::vector contents = std::vector(fileSize);
417 | if (ReadFile(saveFile, &contents.front(), fileSize, &numbytes, NULL)) {
418 | char handle[10] = { NULL };
419 | int index = 0;
420 | for (size_t i = 0; i < fileSize; i++) {
421 | if (contents[i] != ',') {
422 | handle[index] = contents[i];
423 | index++;
424 | }
425 | else {
426 | index = 0;
427 | minimizeWindow(context, reinterpret_cast(std::stoi(std::string(handle))), true);
428 | memset(handle, 0, sizeof(handle));
429 | }
430 | }
431 | TCHAR restoreMessage[MAX_MSG];
432 | _stprintf_s(restoreMessage, i18n[IDS_RESTORE_FROM_UNEXPECTED_TERMINATION], context->iconIndex);
433 | MessageBox(NULL, restoreMessage, APP_NAME, MB_OK);
434 | }
435 | }
436 | }
437 |
438 | static void shutdown(TRCONTEXT* context)
439 | {
440 | CloseHandle(saveFile);
441 | DeleteFile(SAVE_FILE_NAME); // No save file means we have exited gracefully
442 | unhookWinEvent(context);
443 | clearRules(context);
444 | }
445 |
446 | static void onTaskbarRestart(TRCONTEXT* context)
447 | {
448 | NOTIFYICONDATA icon {};
449 | icon.hIcon = context->mainIcon;
450 | createTrayIcon(context->mainWindow, &icon);
451 | for (int i = 0; i < context->iconIndex; i++) {
452 | auto hiddenWindow = &context->icons[i];
453 | if (hiddenWindow->hideType != HideTray) {
454 | continue;
455 | }
456 | Shell_NotifyIcon(NIM_ADD, &hiddenWindow->icon);
457 | Shell_NotifyIcon(NIM_SETVERSION, &hiddenWindow->icon);
458 | }
459 | }
460 |
461 | static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
462 | {
463 | static UINT taskbarRestart;
464 | TRCONTEXT* context = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA));
465 | POINT pt;
466 | switch (uMsg)
467 | {
468 | case WM_CREATE:
469 | taskbarRestart = RegisterWindowMessage(_T("TaskbarCreated"));
470 | break;
471 | case WM_ICON:
472 | if (lParam == WM_LBUTTONDBLCLK) {
473 | restoreWindow(context, wParam);
474 | }
475 | break;
476 | case WM_KEYDOWN:debugf("KEY DOWN"); break;
477 | case WM_OURICON:
478 | switch (lParam) {
479 | case WM_RBUTTONUP:
480 | SetForegroundWindow(hwnd);
481 | GetCursorPos(&pt);
482 | TrackPopupMenuEx(
483 | context->trayMenu,
484 | TPM_BOTTOMALIGN | (GetSystemMetrics(SM_MENUDROPALIGNMENT) ? TPM_RIGHTALIGN : TPM_LEFTALIGN),
485 | pt.x, pt.y, hwnd, NULL
486 | );
487 | break;
488 | case WM_LBUTTONDBLCLK:
489 | showOptionsDlg(context);
490 | break;
491 | }
492 | break;
493 | case WM_MENUCOMMAND:
494 | HMENU menu;
495 | menu = reinterpret_cast(lParam);
496 | if (menu == context->trayMenu) {
497 | MENUITEMINFO mii;
498 | mii.cbSize = sizeof(MENUITEMINFO);
499 | mii.fMask = MIIM_ID | MIIM_DATA;
500 | if (GetMenuItemInfo(menu, wParam, TRUE, &mii)) {
501 | switch (mii.wID) {
502 | case IDM_EXIT:
503 | exitApp();
504 | break;
505 | case IDM_OPTIONS:
506 | showOptionsDlg(context);
507 | break;
508 | case IDM_RESTORE_ALL_WINDOWS:
509 | showAllWindows(context);
510 | break;
511 | case IDM_RESTORE_LAST_WINDOW:
512 | restoreWindow(context, 0, context->icons[context->iconIndex - 1].window);
513 | break;
514 | default:
515 | restoreWindow(context, mii.dwItemData);
516 | break;
517 | }
518 | }
519 | }
520 | break;
521 | case WM_HOTKEY:
522 | switch (wParam) {
523 | case IDHOT_HIDE_WINDOW:
524 | minimizeWindow(context, NULL);
525 | break;
526 | case IDHOT_POPUP_ICONS:
527 | showIconsDlg(context);
528 | break;
529 | case IDHOT_RESTORE_LAST_WINDOW:
530 | restoreWindow(context, 0, context->icons[context->iconIndex - 1].window);
531 | break;
532 | }
533 | break;
534 | default:
535 | if (uMsg == taskbarRestart) {
536 | onTaskbarRestart(context);
537 | }
538 | return DefWindowProc(hwnd, uMsg, wParam, lParam);
539 | }
540 | return 0;
541 | }
542 |
543 | PTCHAR getHotkeyText(PTCHAR text, rsize_t textSize, UINT modifiers, UINT vkey)
544 | {
545 | constexpr TCHAR KEY_WIN[] = _T("Win");
546 | constexpr TCHAR KEY_ALT[] = _T("Alt");
547 | constexpr TCHAR KEY_CTRL[] = _T("Ctrl");
548 | constexpr TCHAR KEY_SHIFT[] = _T("Shift");
549 | constexpr TCHAR KEY_APPEND_ALT[] = _T(" + Alt");
550 | constexpr TCHAR KEY_APPEND_CTRL[] = _T(" + Ctrl");
551 | constexpr TCHAR KEY_APPEND_SHIFT[] = _T(" + Shift");
552 | constexpr TCHAR KEY_PLUS[] = _T(" + ");
553 |
554 | text[0] = NULL;
555 |
556 | if (modifiers & MOD_WIN) {
557 | _tcsnccat_s(text, textSize, KEY_WIN, _countof(KEY_WIN));
558 | }
559 | if (modifiers & MOD_CONTROL) {
560 | _tcsnlen(text, textSize) ?
561 | _tcsnccat_s(text, textSize, KEY_APPEND_CTRL, _countof(KEY_APPEND_CTRL)) :
562 | _tcsnccat_s(text, textSize, KEY_CTRL, _countof(KEY_CTRL));
563 | }
564 | if (modifiers & MOD_SHIFT) {
565 | _tcsnlen(text, textSize) ?
566 | _tcsnccat_s(text, textSize, KEY_APPEND_SHIFT, _countof(KEY_APPEND_SHIFT)) :
567 | _tcsnccat_s(text, textSize, KEY_SHIFT, _countof(KEY_SHIFT));
568 | }
569 | if (modifiers & MOD_ALT) {
570 | _tcsnlen(text, textSize) ?
571 | _tcsnccat_s(text, textSize, KEY_APPEND_ALT, _countof(KEY_APPEND_ALT)) :
572 | _tcsnccat_s(text, textSize, KEY_ALT, _countof(KEY_ALT));
573 | }
574 | size_t len = _tcsnlen(text, textSize);
575 | if (len > 0) {
576 | _tcsnccat_s(text, textSize, KEY_PLUS, _countof(KEY_PLUS));
577 | len += _countof(KEY_PLUS) - 1;
578 | }
579 | GetKeyNameText(MapVirtualKey(vkey, MAPVK_VK_TO_VSC) << 0x10, text + len, textSize - len);
580 | return text;
581 | }
582 |
583 | bool tryRegisterHotkey(HWND hwnd, int id, UINT modifiers, UINT vkey)
584 | {
585 | if (modifiers == 0 || vkey == 0) {
586 | return false;
587 | }
588 | if (RegisterHotKey(hwnd, id, modifiers | MOD_NOREPEAT, vkey)) {
589 | return true;
590 | }
591 |
592 | TCHAR errMsg[MAX_MSG]{ NULL };
593 | TCHAR hotkeyText[MAX_HOTKEY_TEXT]{ NULL };
594 | getHotkeyText(hotkeyText, _countof(hotkeyText) - 1, modifiers, vkey);
595 | _stprintf_s(errMsg, i18n[IDS_HOTKEY_ERROR], hotkeyText);
596 | MessageBox(NULL, errMsg, APP_NAME, MB_OK | MB_ICONWARNING);
597 | return false;
598 | }
599 |
600 | #pragma warning(push)
601 | #pragma warning(disable:4100)
602 | #pragma warning(disable:4189)
603 | #pragma warning(disable:4996)
604 | int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
605 | __LOGGING__;
606 | #pragma warning(pop)
607 |
608 | // Mutex to allow only one instance
609 | HANDLE mutex = CreateMutex(NULL, TRUE, MUTEX_NAME);
610 | if (mutex == NULL) {
611 | MessageBox(NULL, i18n[IDS_MUTEX_ERROR], APP_NAME, MB_OK | MB_ICONERROR);
612 | return 1;
613 | }
614 | else if (GetLastError() == ERROR_ALREADY_EXISTS) {
615 | MessageBox(NULL, i18n[IDS_ALREADY_RUNNING], APP_NAME, MB_OK | MB_ICONERROR);
616 | return 1;
617 | }
618 |
619 | WNDCLASS wc = {};
620 | wc.lpfnWndProc = WindowProc;
621 | wc.hInstance = hInstance;
622 | wc.lpszClassName = APP_NAME;
623 | if (!RegisterClass(&wc)) {
624 | return 1;
625 | }
626 |
627 | GetClassInfo(NULL, WC_DIALOG, &wc);
628 | wc.style |= CS_DROPSHADOW;
629 | wc.lpszClassName = _T(POPUP_CLASS);
630 | if (!RegisterClass(&wc)) {
631 | return 1;
632 | }
633 |
634 | auto context = &appContext;
635 | context->instance = hInstance;
636 | context->cmdLine = GetCommandLine();
637 | loadOptions(context);
638 |
639 | context->mainWindow = CreateWindow(APP_NAME, NULL, NULL, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
640 | if (!context->mainWindow) {
641 | return 1;
642 | }
643 | ShowWindow(context->mainWindow, SW_HIDE);
644 |
645 | // Store our context in main window for retrieval by WindowProc
646 | SetWindowLongPtr(context->mainWindow, GWLP_USERDATA, reinterpret_cast(context));
647 |
648 | NOTIFYICONDATA icon = {};
649 | context->mainIcon = icon.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TRAYMOND));
650 | createTrayIcon(context->mainWindow, &icon);
651 | context->trayMenu = createTrayMenu(context);
652 | startup(context);
653 |
654 | tryRegisterHotkey(context->mainWindow, IDHOT_HIDE_WINDOW, context->hotkey.modifiers, context->hotkey.vkey);
655 | tryRegisterHotkey(context->mainWindow, IDHOT_POPUP_ICONS, context->hotkey2.modifiers, context->hotkey2.vkey);
656 | tryRegisterHotkey(context->mainWindow, IDHOT_RESTORE_LAST_WINDOW, context->hotkey3.modifiers, context->hotkey3.vkey);
657 |
658 | BOOL bRet;
659 | MSG msg;
660 | while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
661 | {
662 | if (bRet != -1) {
663 | TranslateMessage(&msg);
664 | DispatchMessage(&msg);
665 | }
666 | }
667 | // Clean up on exit;
668 | UnregisterHotKey(context->mainWindow, IDHOT_HIDE_WINDOW);
669 | UnregisterHotKey(context->mainWindow, IDHOT_POPUP_ICONS);
670 | UnregisterHotKey(context->mainWindow, IDHOT_RESTORE_LAST_WINDOW);
671 | showAllWindows(context);
672 | Shell_NotifyIcon(NIM_DELETE, &icon);
673 | DestroyMenu(context->trayMenu);
674 | DestroyWindow(context->mainWindow);
675 | shutdown(context);
676 | ReleaseMutex(mutex);
677 | CloseHandle(mutex);
678 | return msg.wParam;
679 | }
680 |
681 | TRCONTEXT* AppContext()
682 | {
683 | return &appContext;
684 | }
685 |
--------------------------------------------------------------------------------
/src/traymond.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "resource.h"
8 | #include "i18n.h"
9 |
10 |
11 | #define VK_Z_KEY 0x5A
12 | #define VK_Q_KEY 0x51
13 | // These keys are used to send windows to tray
14 | #define TRAY_KEY VK_Z_KEY
15 | #define TRAY_KEY2 VK_Q_KEY
16 | #define MOD_KEY MOD_WIN + MOD_SHIFT
17 |
18 | #define WM_ICON 0x1C0A
19 | #define WM_OURICON 0x1C0B
20 | #define MAXIMUM_WINDOWS 100
21 | #define IDHOT_HIDE_WINDOW 0
22 | #define IDHOT_POPUP_ICONS 1
23 | #define IDHOT_RESTORE_LAST_WINDOW 2
24 | #define TEST_HOTKEY_ID 0xBFFF
25 | #define MAX_MSG 1024
26 | #define MAX_HOTKEY_TEXT 64
27 | #define MAX_WINDOW_TEXT 128
28 | #define MAX_CLASS_NAME 256
29 | #define MAX_RULE_NAME 128
30 |
31 | #define APP_NAME _T(PROJECT_NAME)
32 | #define SAVE_FILE_NAME _T(PROJECT_NAME_LC ".dat")
33 | #define MUTEX_NAME _T(PROJECT_NAME_LC "_mutex")
34 | #define REG_KEY_SOFTWARE _T("SOFTWARE\\" PROJECT_NAME)
35 | #define REG_KEY_RUN _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")
36 | #define HELP_URL _T("https://github.com/tabris17/traymond/wiki")
37 |
38 | #define APPLICATION_ERROR_CODE 0x10000000
39 | #define TOP_LEVEL_WINDOW_ERROR (APPLICATION_ERROR_CODE | 1)
40 |
41 | #define SET_CONTAINS(_THE_SET_, _THE_VALUE_) ((_THE_SET_).find(_THE_VALUE_) != (_THE_SET_).end())
42 | #define SET_NOT_CONTAINS(_THE_SET_, _THE_VALUE_) ((_THE_SET_).find(_THE_VALUE_) == (_THE_SET_).end())
43 | #define VECTOR_ERASE(_THE_VECTOR_, _THE_INDEX_) ((_THE_VECTOR_).erase((_THE_VECTOR_).begin() + (_THE_INDEX_)))
44 | #define RULE_IS_ALWAYS_NOTIFY(_FLAG_) (_FLAG_ & RULE_ALWAYS_NOTIFY)
45 | #define RULE_IS_NOTIFY_FIRST_TIME(_FLAG_) (_FLAG_ & RULE_NOTIFY_FIRST_TIME)
46 |
47 | typedef BOOL(WINAPI *IsTopLevelWindow)(HWND hwnd);
48 |
49 | typedef struct {
50 | UINT modifiers;
51 | UINT vkey;
52 | } HOTKEY;
53 |
54 | typedef enum {
55 | HideTray = 0,
56 | HideMenu = 1,
57 | } HIDE_TYPE;
58 |
59 | typedef struct {
60 | MENUITEMINFO info;
61 | TCHAR caption[MAX_WINDOW_TEXT];
62 | } MENUITEMDATA;
63 |
64 | // Stores hidden window record.
65 | typedef struct HIDDEN_WINDOW {
66 | HIDE_TYPE hideType;
67 | union {
68 | NOTIFYICONDATA icon;
69 | MENUITEMDATA menu;
70 | };
71 | HWND window;
72 | } HIDDEN_WINDOW;
73 |
74 | #define RULE_REGEX_WINDOW_TEXT 1
75 | #define RULE_REGEX_WINDOW_CLASS (1 << 8)
76 | #define RULE_REGEX_EXE_FILENAME (1 << 16)
77 | #define RULE_ON_MINIMIZE (1 << 1)
78 | #define RULE_AUTO_OFF (1 << 2)
79 | #define RULE_NOTIFY_FIRST_TIME (1 << 24)
80 | #define RULE_ALWAYS_NOTIFY (1 << 23)
81 |
82 | // Auto hiding window rule
83 | #pragma warning(push)
84 | #pragma warning(disable:4200)
85 | typedef struct HIDING_RULE {
86 | size_t size;
87 | DWORD flag;
88 | TCHAR ruleData[0];
89 | } HIDING_RULE;
90 | #pragma warning(push)
91 |
92 | typedef std::unordered_set HWND_SET;
93 |
94 | typedef std::vector HIDING_RULES;
95 |
96 | // Current execution context
97 | typedef struct TRCONTEXT {
98 | HIDE_TYPE hideType;
99 | BOOL autorun;
100 | BOOL autoHiding;
101 | HOTKEY hotkey;
102 | HOTKEY hotkey2;
103 | HOTKEY hotkey3;
104 | HWINEVENTHOOK hook;
105 | HICON mainIcon;
106 | HINSTANCE instance;
107 | LPTSTR cmdLine;
108 | HWND mainWindow;
109 | HIDDEN_WINDOW icons[MAXIMUM_WINDOWS];
110 | HMENU trayMenu;
111 | int iconIndex; // How many windows are currently hidden
112 | HWND_SET freeWindows;
113 | HWND_SET hiddenWindows;
114 | HIDING_RULES hidingRules;
115 | } TRCONTEXT;
116 |
117 | int reviseHiddenWindowIcon(TRCONTEXT* context);
118 | bool minimizeWindow(TRCONTEXT* context, HWND currWin, bool restored = false);
119 | bool restoreWindow(TRCONTEXT* context, UINT xID, HWND hwnd = NULL);
120 | PTCHAR getHotkeyText(PTCHAR text, rsize_t textSize, UINT modifiers, UINT vkey);
121 | bool tryRegisterHotkey(HWND hwnd, int id, UINT modifiers, UINT vkey);
122 | HICON getWindowIcon(const TRCONTEXT* context, HWND hwnd);
123 | BOOL notifyHidingWindow(TRCONTEXT* context, HWND hwnd);
124 |
125 | TRCONTEXT* AppContext();
126 |
127 | #ifdef TRAYMON_MAIN
128 | IsTopLevelWindow isTopLevelWindow = NULL;
129 | I18n i18n;
130 | #else
131 | extern IsTopLevelWindow isTopLevelWindow;
132 | extern I18n i18n;
133 | #endif
134 |
--------------------------------------------------------------------------------
/src/winevent.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "winevent.h"
5 | #include "traymond.h"
6 | #include "logging.h"
7 | #include "rules.h"
8 |
9 |
10 | DWORD GetWindowExeFileName(HWND hwnd, PTCHAR fileName, DWORD size)
11 | {
12 | DWORD processId;
13 | if (0 == GetWindowThreadProcessId(hwnd, &processId)) {
14 | return FALSE;
15 | }
16 | HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
17 | if (NULL == process) {
18 | return FALSE;
19 | }
20 | auto result = GetModuleFileNameEx(process, NULL, fileName, size);
21 | CloseHandle(process);
22 | return result;
23 | }
24 |
25 | #pragma warning(push)
26 | #pragma warning(disable:4100)
27 | static void WINAPI DefaultWinEventProc(HWINEVENTHOOK hWinEventHook,
28 | DWORD event, HWND hwnd,
29 | LONG idObject, LONG idChild,
30 | DWORD idEventThread, DWORD dwmsEventTime)
31 | #pragma warning(pop)
32 | {
33 | if (event != EVENT_OBJECT_DESTROY) return;
34 |
35 | auto context = AppContext();
36 | context->freeWindows.erase(hwnd);
37 |
38 | if (SET_CONTAINS(context->hiddenWindows, hwnd)) {
39 | restoreWindow(context, 0, hwnd);
40 | }
41 | }
42 |
43 |
44 | static void WINAPI AutoHidingWinEventProc(HWINEVENTHOOK hWinEventHook,
45 | DWORD event, HWND hwnd,
46 | LONG idObject, LONG idChild,
47 | DWORD idEventThread, DWORD dwmsEventTime)
48 | {
49 | bool isMinimizing = false;
50 | switch (event) {
51 | case EVENT_SYSTEM_MINIMIZESTART:
52 | isMinimizing = true;
53 | break;
54 | case EVENT_OBJECT_CREATE:
55 | break;
56 | case EVENT_OBJECT_DESTROY:
57 | return DefaultWinEventProc(
58 | hWinEventHook,
59 | event,
60 | hwnd,
61 | idObject,
62 | idChild,
63 | idEventThread,
64 | dwmsEventTime
65 | );
66 | case EVENT_OBJECT_SHOW:
67 | break;
68 | case EVENT_OBJECT_NAMECHANGE:
69 | break;
70 | default:
71 | return;
72 | }
73 |
74 | if ((OBJID_WINDOW != idObject || CHILDID_SELF != idChild) ||
75 | !isTopLevelWindow(hwnd) || !IsWindowVisible(hwnd)) {
76 |
77 | return;
78 | }
79 |
80 | auto context = AppContext();
81 | HIDING_RULE *rule = nullptr;
82 | if ((isMinimizing || SET_NOT_CONTAINS(context->freeWindows, hwnd)) &&
83 | matchRule(context, hwnd, isMinimizing, &rule)) {
84 | bool isFirstTime = SET_NOT_CONTAINS(context->freeWindows, hwnd);
85 | minimizeWindow(context, hwnd);
86 | if (RULE_IS_ALWAYS_NOTIFY(rule->flag) ||
87 | RULE_IS_NOTIFY_FIRST_TIME(rule->flag) && isFirstTime) {
88 |
89 | notifyHidingWindow(context, hwnd);
90 | }
91 | }
92 | }
93 |
94 |
95 | void hookWinEvent(TRCONTEXT* context)
96 | {
97 | context->hook = SetWinEventHook(
98 | EVENT_SYSTEM_MINIMIZESTART,
99 | EVENT_OBJECT_NAMECHANGE,
100 | NULL,
101 | context->autoHiding ? AutoHidingWinEventProc : DefaultWinEventProc,
102 | 0, 0,
103 | WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS
104 | );
105 | }
106 |
107 | void unhookWinEvent(TRCONTEXT* context)
108 | {
109 | if (context->hook) {
110 | UnhookWinEvent(context->hook);
111 | }
112 | }
113 |
114 |
--------------------------------------------------------------------------------
/src/winevent.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "traymond.h"
4 |
5 | void hookWinEvent(TRCONTEXT* context);
6 | void unhookWinEvent(TRCONTEXT* context);
7 | DWORD GetWindowExeFileName(HWND hwnd, PTCHAR fileName, DWORD size);
8 |
--------------------------------------------------------------------------------
/traymond.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.11.35327.3
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "traymond", "traymond.vcxproj", "{7FDF991D-57D7-48AE-BDAC-8DD9BE849241}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "traymond.locale.en_US", "locale\en_US\en_US.vcxproj", "{EE43692D-0E29-4699-9007-7F1D41BB38D5}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|x64 = Debug|x64
13 | Debug|x86 = Debug|x86
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Debug|x64.ActiveCfg = Debug|x64
19 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Debug|x64.Build.0 = Debug|x64
20 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Debug|x86.ActiveCfg = Debug|Win32
21 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Debug|x86.Build.0 = Debug|Win32
22 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Release|x64.ActiveCfg = Release|x64
23 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Release|x64.Build.0 = Release|x64
24 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Release|x86.ActiveCfg = Release|Win32
25 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}.Release|x86.Build.0 = Release|Win32
26 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Debug|x64.ActiveCfg = Debug|x64
27 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Debug|x64.Build.0 = Debug|x64
28 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Debug|x86.ActiveCfg = Debug|Win32
29 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Debug|x86.Build.0 = Debug|Win32
30 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Release|x64.ActiveCfg = Release|x64
31 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Release|x64.Build.0 = Release|x64
32 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Release|x86.ActiveCfg = Release|Win32
33 | {EE43692D-0E29-4699-9007-7F1D41BB38D5}.Release|x86.Build.0 = Release|Win32
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(ExtensibilityGlobals) = postSolution
39 | SolutionGuid = {2C35F167-6088-4BEE-84A4-473B6072CCA2}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/traymond.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 15.0
23 | {7FDF991D-57D7-48AE-BDAC-8DD9BE849241}
24 | Win32Proj
25 | 10.0
26 |
27 |
28 |
29 | Application
30 | true
31 | v143
32 |
33 |
34 | Application
35 | true
36 | v143
37 |
38 |
39 | Application
40 | false
41 | v143
42 |
43 |
44 | Application
45 | false
46 | v143
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Traymond
68 |
69 |
70 | false
71 | Traymond
72 |
73 |
74 | false
75 | Traymond
76 |
77 |
78 |
79 |
80 |
81 |
82 | Level4
83 | _UNICODE;UNICODE;_DEBUG;DEBUG;%(PreprocessorDefinitions)
84 |
85 |
86 |
87 | "type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'";%(AdditionalManifestDependencies)
88 | comctl32.lib;%(AdditionalDependencies)
89 |
90 |
91 | true
92 |
93 |
94 |
95 |
96 |
97 |
98 | Level4
99 |
100 |
101 |
102 |
103 |
104 | Level4
105 | true
106 | true
107 | _UNICODE;UNICODE;%(PreprocessorDefinitions)
108 | None
109 |
110 |
111 | UseLinkTimeCodeGeneration
112 |
113 |
114 | "type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'";%(AdditionalManifestDependencies)
115 | comctl32.lib;%(AdditionalDependencies)
116 | false
117 |
118 |
119 | true
120 |
121 |
122 |
123 |
124 |
125 |
126 | Level4
127 | true
128 | true
129 |
130 |
131 | UseLinkTimeCodeGeneration
132 |
133 |
134 | true
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/traymond.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 | Source Files
32 |
33 |
34 | Source Files
35 |
36 |
37 |
38 |
39 | Resource Files
40 |
41 |
42 |
43 |
44 | Header Files
45 |
46 |
47 | Header Files
48 |
49 |
50 | Header Files
51 |
52 |
53 | Header Files
54 |
55 |
56 | Header Files
57 |
58 |
59 | Header Files
60 |
61 |
62 | Header Files
63 |
64 |
65 | Header Files
66 |
67 |
68 |
69 |
70 | Resource Files
71 |
72 |
73 |
--------------------------------------------------------------------------------