w-mousekey 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.js +55 -0
  4. package/.jsdoc +25 -0
  5. package/AutoHotkey_2.0.19/AutoHotkey.chm +0 -0
  6. package/AutoHotkey_2.0.19/AutoHotkey32.exe +0 -0
  7. package/AutoHotkey_2.0.19/AutoHotkey64.exe +0 -0
  8. package/AutoHotkey_2.0.19/Install.cmd +2 -0
  9. package/AutoHotkey_2.0.19/UX/Templates/Minimal for v2.ahk +6 -0
  10. package/AutoHotkey_2.0.19/UX/WindowSpy.ahk +246 -0
  11. package/AutoHotkey_2.0.19/UX/inc/CommandLineToArgs.ahk +12 -0
  12. package/AutoHotkey_2.0.19/UX/inc/CreateAppShortcut.ahk +37 -0
  13. package/AutoHotkey_2.0.19/UX/inc/EnableUIAccess.ahk +246 -0
  14. package/AutoHotkey_2.0.19/UX/inc/GetGitHubReleaseAssetURL.ahk +26 -0
  15. package/AutoHotkey_2.0.19/UX/inc/HashFile.ahk +96 -0
  16. package/AutoHotkey_2.0.19/UX/inc/README.txt +3 -0
  17. package/AutoHotkey_2.0.19/UX/inc/ShellRun.ahk +7 -0
  18. package/AutoHotkey_2.0.19/UX/inc/bounce-v1.ahk +3 -0
  19. package/AutoHotkey_2.0.19/UX/inc/common.ahk +21 -0
  20. package/AutoHotkey_2.0.19/UX/inc/config.ahk +13 -0
  21. package/AutoHotkey_2.0.19/UX/inc/identify.ahk +29 -0
  22. package/AutoHotkey_2.0.19/UX/inc/identify_regex.ahk +4 -0
  23. package/AutoHotkey_2.0.19/UX/inc/launcher-common.ahk +78 -0
  24. package/AutoHotkey_2.0.19/UX/inc/spy.ico +0 -0
  25. package/AutoHotkey_2.0.19/UX/inc/ui-base.ahk +129 -0
  26. package/AutoHotkey_2.0.19/UX/install-ahk2exe.ahk +53 -0
  27. package/AutoHotkey_2.0.19/UX/install-version.ahk +109 -0
  28. package/AutoHotkey_2.0.19/UX/install.ahk +1056 -0
  29. package/AutoHotkey_2.0.19/UX/launcher.ahk +430 -0
  30. package/AutoHotkey_2.0.19/UX/reload-v1.ahk +22 -0
  31. package/AutoHotkey_2.0.19/UX/reset-assoc.ahk +38 -0
  32. package/AutoHotkey_2.0.19/UX/ui-dash.ahk +200 -0
  33. package/AutoHotkey_2.0.19/UX/ui-editor.ahk +238 -0
  34. package/AutoHotkey_2.0.19/UX/ui-launcherconfig.ahk +195 -0
  35. package/AutoHotkey_2.0.19/UX/ui-newscript.ahk +296 -0
  36. package/AutoHotkey_2.0.19/UX/ui-setup.ahk +175 -0
  37. package/AutoHotkey_2.0.19/UX/ui-uninstall.ahk +68 -0
  38. package/AutoHotkey_2.0.19/WindowSpy.ahk +2 -0
  39. package/AutoHotkey_2.0.19/license.txt +351 -0
  40. package/AutoHotkey_2.0.19/zclick.ahk +5 -0
  41. package/AutoHotkey_2.0.19/zrun_click.bat +2 -0
  42. package/AutoHotkey_2.0.19/zrun_send.bat +2 -0
  43. package/AutoHotkey_2.0.19/zsend.ahk +4 -0
  44. package/LICENSE +21 -0
  45. package/README.md +23 -0
  46. package/SECURITY.md +5 -0
  47. package/_tmp/view.all.png +0 -0
  48. package/_tmp/view.det.png +0 -0
  49. package/babel.config.js +16 -0
  50. package/dist/action.umd.js +32 -0
  51. package/dist/action.umd.js.map +1 -0
  52. package/dist/ck-pic.umd.js +7 -0
  53. package/dist/ck-pic.umd.js.map +1 -0
  54. package/dist/compare.umd.js +7 -0
  55. package/dist/compare.umd.js.map +1 -0
  56. package/dist/get-settings.umd.js +7 -0
  57. package/dist/get-settings.umd.js.map +1 -0
  58. package/dist/mousekey.umd.js +7 -0
  59. package/dist/mousekey.umd.js.map +1 -0
  60. package/dist/screen.umd.js +7 -0
  61. package/dist/screen.umd.js.map +1 -0
  62. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  63. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  64. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  65. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  66. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  67. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  68. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  69. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  70. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  71. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  72. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  73. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  74. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  75. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  76. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  77. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  78. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  79. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  80. package/docs/index.html +84 -0
  81. package/docs/scripts/collapse.js +39 -0
  82. package/docs/scripts/commonNav.js +28 -0
  83. package/docs/scripts/linenumber.js +25 -0
  84. package/docs/scripts/nav.js +12 -0
  85. package/docs/scripts/polyfill.js +4 -0
  86. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  87. package/docs/scripts/prettify/lang-css.js +2 -0
  88. package/docs/scripts/prettify/prettify.js +28 -0
  89. package/docs/scripts/search.js +99 -0
  90. package/docs/styles/jsdoc.css +776 -0
  91. package/docs/styles/prettify.css +80 -0
  92. package/g.mjs +46 -0
  93. package/g.test_opencv.mjs +36 -0
  94. package/package.json +41 -0
  95. package/pic/view.png +0 -0
  96. package/script.txt +17 -0
  97. package/src/action.mjs +132 -0
  98. package/src/ckPic.mjs +95 -0
  99. package/src/compare.mjs +201 -0
  100. package/src/getSettings.mjs +17 -0
  101. package/src/mousekey.mjs +95 -0
  102. package/src/screen.mjs +62 -0
  103. package/test/WMousekey.test.mjs +11 -0
  104. package/toolg/addVersion.mjs +4 -0
  105. package/toolg/cleanFolder.mjs +4 -0
  106. package/toolg/gDistRollup.mjs +33 -0
  107. package/toolg/modifyReadme.mjs +4 -0
@@ -0,0 +1,1056 @@
1
+ ; This script contains AutoHotkey (un)installation routines.
2
+ ; See the AutoHotkey v2 documentation for usage.
3
+ #include inc\bounce-v1.ahk
4
+ /* v1 stops here */
5
+ #requires AutoHotkey v2.0
6
+
7
+ #SingleInstance Off ; Needed for elevation with *runas.
8
+
9
+ #include inc\launcher-common.ahk
10
+ #include inc\HashFile.ahk
11
+ #include inc\CreateAppShortcut.ahk
12
+ #include inc\EnableUIAccess.ahk
13
+ #include inc\ShellRun.ahk
14
+
15
+ if A_LineFile = A_ScriptFullPath
16
+ Install_Main
17
+
18
+ Install_Main() {
19
+ try {
20
+ Installation.Instance := inst := Installation()
21
+ method := 'InstallFull'
22
+ params := []
23
+ while A_Index <= A_Args.Length {
24
+ switch A_Args[A_Index], 'off' {
25
+ case '/install':
26
+ method := 'InstallExtraVersion'
27
+ inst.SourceDir := A_Args[++A_Index]
28
+ case '/uninstall':
29
+ method := 'Uninstall'
30
+ if A_Index < A_Args.Length && SubStr(A_Args[A_Index+1],1,1) != '/'
31
+ params.Push(A_Args[++A_Index])
32
+ case '/to', '/installto':
33
+ inst.InstallDir := A_Args[++A_Index]
34
+ case '/elevate':
35
+ inst.RequireAdmin := true
36
+ case '/user':
37
+ inst.UserInstall := true
38
+ case '/silent':
39
+ inst.Silent := true
40
+ default:
41
+ MsgBox 'Invalid arg "' A_Args[A_Index] '"', inst.DialogTitle, "Iconx"
42
+ ExitApp 1
43
+ }
44
+ }
45
+ inst.%method%(params*)
46
+ }
47
+ catch as e {
48
+ inst.ErrorBox(e, "&Exit")
49
+ ExitApp 1
50
+ }
51
+ }
52
+
53
+ class Installation {
54
+ ProductName := "AutoHotkey"
55
+ ProductURL := "https://autohotkey.com"
56
+ Publisher := "AutoHotkey Foundation LLC"
57
+ Version := A_AhkVersion
58
+ AppUserModelID := 'AutoHotkey.AutoHotkey'
59
+
60
+ UserInstall := !A_IsAdmin
61
+ Interpreter := A_AhkPath
62
+ Silent := false
63
+
64
+ ScriptProgId := 'AutoHotkeyScript'
65
+ SoftwareSubKey := 'Software\AutoHotkey'
66
+ RootKey => this.UserInstall ? 'HKCU' : 'HKLM'
67
+ SoftwareKey => this.RootKey '\' this.SoftwareSubKey
68
+ ClassesKey => this.RootKey '\Software\Classes'
69
+ FileTypeKey => this.ClassesKey '\' this.ScriptProgId
70
+ UninstallKey => this.RootKey '\Software\Microsoft\Windows\CurrentVersion\Uninstall\AutoHotkey'
71
+ StartFolder => (this.UserInstall ? A_Programs : A_ProgramsCommon)
72
+ UninstallCmd => this.CmdStr('UX\ui-uninstall.ahk', ((A_IsAdmin && this.UserInstall) ? '/elevate' : ''))
73
+ QUninstallCmd => this.CmdStr('UX\install.ahk', '/uninstall /silent')
74
+
75
+ DialogTitle => this.ProductName " Setup"
76
+
77
+ FileItems := [] ; [{Source, Dest}]
78
+ RegItems := [] ; [{Key, ValueName, Value}]
79
+ PreCheck := [] ; [Callback(this)]
80
+ PreAction := [] ; [Callback(this)]
81
+ PostAction := [] ; [Callback(this)]
82
+
83
+ ResolveInstallDir() {
84
+ if hadInstallDir := this.HasProp('InstallDir')
85
+ DirCreate installDir := this.InstallDir
86
+ else
87
+ installDir := IsSet(InstallUtil) ? InstallUtil.DefaultDir : A_ScriptDir '\..'
88
+ Loop Files installDir, 'D'
89
+ installDir := A_LoopFileFullPath
90
+ this.InstallDir := installDir
91
+ SetRegView 64
92
+ installDirs := []
93
+ for rootKey in ['HKLM', 'HKCU']
94
+ installDirs.Push RegRead(rootKey '\' this.SoftwareSubKey, 'InstallDir', '')
95
+ ; Override installation mode if already installed here
96
+ if installDirs[1] = installDir
97
+ this.UserInstall := false
98
+ else if installDirs[2] = installDir
99
+ this.UserInstall := true
100
+ ; If this.InstallDir wasn't set upon entry to this method...
101
+ else if !hadInstallDir {
102
+ ; Default to the location of an existing installation matching this.UserInstall
103
+ if installDirs[this.UserInstall?2:1]
104
+ this.InstallDir := installDirs[this.UserInstall?2:1]
105
+ ; Default to the location and mode of any other existing installation
106
+ else if installDirs[this.UserInstall?1:2] {
107
+ if !A_IsAdmin && this.UserInstall {
108
+ ; Use the existing all-user installation only if elevation is successful
109
+ try
110
+ RunWait '*runas ' DllCall('GetCommandLine', 'str')
111
+ catch
112
+ return ; Presume user cancelled; continue as user
113
+ else
114
+ ExitApp
115
+ }
116
+ this.InstallDir := installDirs[this.UserInstall?1:2], this.UserInstall := !this.UserInstall
117
+ }
118
+ }
119
+ }
120
+
121
+ ResolveSourceDir() {
122
+ if !this.HasProp('SourceDir') {
123
+ if A_IsCompiled && IsSet(UnpackFiles)
124
+ this.SourceDir := UnpackFiles(this.InstallDir)
125
+ else
126
+ this.SourceDir := A_ScriptDir '\..'
127
+ }
128
+ Loop Files this.SourceDir, 'D'
129
+ this.SourceDir := A_LoopFileFullPath
130
+ else
131
+ throw ValueError("Invalid source directory",, this.SourceDir)
132
+ }
133
+
134
+ HashesPath => this.InstallDir '\UX\installed-files.csv'
135
+ Hashes => (
136
+ this.DefineProp('Hashes', {value: hashes := this.ReadHashes()}),
137
+ hashes
138
+ )
139
+
140
+ Apply() {
141
+ if !DirExist(this.InstallDir)
142
+ DirCreate this.InstallDir
143
+ SetWorkingDir this.InstallDir
144
+
145
+ ; Execute pre-check actions
146
+ for item in this.PreCheck
147
+ item(this)
148
+
149
+ ; Detect possible conflicts before taking action
150
+ this.PreApplyChecks()
151
+
152
+ ; Execute pre-install actions
153
+ for item in this.PreAction
154
+ item(this)
155
+
156
+ ; Install files
157
+ for item in this.FileItems {
158
+ SplitPath item.Dest,, &destDir
159
+ if destDir != ''
160
+ DirCreate destDir
161
+ try
162
+ FileCopy item.Source, item.Dest, true
163
+ catch
164
+ this.WarnBox 'Copy failed`nsource: ' item.Source '`ndest: ' item.Dest
165
+ else {
166
+ ; If source files were extracted from a zip, they may have a Zone.Identifier
167
+ ; stream identifying them as coming from the Internet. These must be deleted
168
+ ; to prevent warnings when the user runs the installed files.
169
+ try FileDelete item.Dest ":Zone.Identifier"
170
+ this.AddFileHash(item.Dest, this.Version)
171
+ }
172
+ }
173
+
174
+ ; Install registry settings
175
+ for item in this.RegItems {
176
+ if item.HasProp('Value') {
177
+ RegWrite(item.Value, item.Value is Integer ? 'REG_DWORD' : 'REG_SZ'
178
+ , item.Key, item.ValueName)
179
+ } else {
180
+ try RegDelete(item.Key, item.ValueName)
181
+ }
182
+ }
183
+
184
+ ; Execute post-install actions
185
+ for item in this.PostAction
186
+ try
187
+ item(this)
188
+ catch as e
189
+ this.ErrorBox(e, "&Continue")
190
+
191
+ ; Write file list to disk
192
+ if this.Hashes.Count
193
+ this.WriteHashes
194
+ }
195
+
196
+ ElevationNeeded => !A_IsAdmin && (!this.UserInstall || this.HasProp('RequireAdmin'))
197
+
198
+ ElevateIfNeeded() {
199
+ if this.ElevationNeeded {
200
+ try RunWait '*runas ' DllCall('GetCommandLine', 'str')
201
+ ExitApp
202
+ }
203
+ }
204
+
205
+ ;{ Installation entry points
206
+
207
+ InstallFull() {
208
+ SetRegView 64
209
+
210
+ this.ResolveInstallDir
211
+ this.ResolveSourceDir
212
+
213
+ doFiles := this.InstallDir != this.SourceDir
214
+
215
+ ; If a newer version is already installed, integrate with it
216
+ ux := doFiles && this.GetTargetUX()
217
+ if ux && VerCompare(ux.Version, this.Version) > 0 {
218
+ cmd := StrReplace(ux.InstallCommand, '%1', this.SourceDir,, &replaced)
219
+ if !replaced
220
+ cmd .= ' "' this.SourceDir '"'
221
+ if this.ElevationNeeded
222
+ cmd := '*runas ' cmd
223
+ try
224
+ exitCode := RunWait(cmd, this.InstallDir)
225
+ catch as e
226
+ if InStr(e.Message e.Extra, 'cancel')
227
+ exitCode := 1
228
+ else
229
+ throw e
230
+ ExitApp exitCode
231
+ }
232
+
233
+ this.ElevateIfNeeded
234
+
235
+ ; If a legacy version is installed, upgrade it
236
+ wowKey(k) => StrReplace(k, '\Software\', '\Software\Wow6432Node\')
237
+ installedVersion := RegRead(key := wowKey(this.SoftwareKey), 'Version', '')
238
+ || RegRead(key := this.SoftwareKey, 'Version', '')
239
+ if SubStr(installedVersion, 1, 2) = '1.' {
240
+ this.SoftwareKeyV1 := key
241
+ this.UninstallKeyV1 := InStr(key, 'Wow64') ? wowKey(this.UninstallKey) : this.UninstallKey
242
+ this.AddPreCheck this.PrepareUpgradeV1.Bind(, installedVersion)
243
+ this.AddPreAction this.UpgradeV1.Bind(, installedVersion)
244
+ }
245
+
246
+ if doFiles || FileExist(this.InstallDir '\UX\AutoHotkeyUX.exe')
247
+ this.Interpreter := this.InstallDir '\UX\AutoHotkeyUX.exe'
248
+
249
+ if doFiles {
250
+ if this.GetLinkAttrib(this.InstallDir '\v2')
251
+ this.AddPreAction this.DeleteLink.Bind(, 'v2')
252
+ this.AddCoreFiles 'v2'
253
+ ; Give UX its own AutoHotkey.exe for a few reasons:
254
+ ; 1. The Start menu shortcut needs a stable path, since pinning to taskbar creates
255
+ ; a copy that won't get updated (obsolete since using 'v2' and DisplaceFile now).
256
+ ; 2. LauncherConfigGui may store the path under HKCU, which mightn't get updated.
257
+ ; 3. It helps differentiate the launcher from other scripts in Task Manager.
258
+ ; 4. It makes the UX scripts independent from the installed interpreters.
259
+ srcExe := this.SourceDir '\AutoHotkey' (A_Is64bitOS ? '64' : '32') '.exe'
260
+ dstExe := this.InstallDir '\UX\AutoHotkeyUX.exe'
261
+ if FileExist(srcExe) || (!FileExist(dstExe) && (srcExe := A_AhkPath))
262
+ this.AddFileCopy srcExe, 'UX\AutoHotkeyUX.exe' ; Must be relative
263
+ this.AddPostAction this.UpdateV2Link
264
+
265
+ this.AddUXFiles
266
+ this.AddMiscFiles
267
+ }
268
+
269
+ this.AddPostAction this.CreateWindowSpyRedirect
270
+
271
+ this.AddUninstallReg
272
+ this.AddSoftwareReg
273
+ this.AddFileTypeReg
274
+
275
+ this.Apply
276
+
277
+ if FileExist(this.InstallDir '\UX\reset-assoc.ahk')
278
+ RunWait this.CmdStr('UX\reset-assoc.ahk', '/check')
279
+
280
+ if !this.Silent
281
+ ShellRun this.Interpreter, 'UX\ui-dash.ahk', this.InstallDir
282
+ }
283
+
284
+ InstallExtraVersion() {
285
+ SetRegView 64
286
+
287
+ this.ResolveInstallDir
288
+ this.ResolveSourceDir
289
+
290
+ Loop Files this.SourceDir '\AutoHotkey*.exe' {
291
+ exe := GetExeInfo(A_LoopFilePath)
292
+ break
293
+ } else
294
+ throw Error("AutoHotkey*.exe not found in source directory",, this.SourceDir)
295
+
296
+ this.ElevateIfNeeded
297
+
298
+ coreDir := 'v' (this.Version := exe.Version)
299
+
300
+ if VerCompare(this.Version, '2.0-') < 0 {
301
+ try
302
+ exe := GetExeInfo(this.InstallDir '\AutoHotkeyU32.exe')
303
+ catch
304
+ {}
305
+ else if VerCompare(this.Version, exe.Version) > 0
306
+ && !FileExist(this.InstallDir '\v' exe.Version) {
307
+ this.AddPreAction this.DisplaceV1.Bind(, exe.Version)
308
+ coreDir := '.'
309
+ }
310
+ }
311
+
312
+ this.AddCoreFiles(coreDir)
313
+
314
+ if FileExist(this.SourceDir '\Compiler\Ahk2Exe.exe') {
315
+ compilerVersion := GetExeInfo(this.SourceDir '\Compiler\Ahk2Exe.exe').Version
316
+ installedCompiler := this.Hashes.Get('Compiler\Ahk2Exe.exe', '')
317
+ if !installedCompiler || VerCompare(compilerVersion, installedCompiler.Version) > 0
318
+ this.AddCompiler(this.SourceDir '\Compiler')
319
+ }
320
+
321
+ this.Apply
322
+ }
323
+
324
+ ;}
325
+
326
+ ;{ Uninstallation
327
+
328
+ UninstallRegistry() {
329
+ SetRegView 64
330
+ delKey this.FileTypeKey
331
+ delKey this.ClassesKey '\.ahk'
332
+ delKey this.SoftwareKey
333
+ delKey this.UninstallKey
334
+ if this.RootKey = 'HKLM' {
335
+ delKey 'HKCU\' this.SoftwareSubKey
336
+ delKey 'HKCU\Software\Classes\' this.ScriptProgId
337
+ for k in ['AutoHotkey.exe', 'Ahk2Exe.exe'] ; made by v1 installer
338
+ delKey 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\' k
339
+ }
340
+
341
+ delKey(key) {
342
+ try
343
+ RegDeleteKey key
344
+ catch OSError as e
345
+ if e.number != 2 ; ERROR_FILE_NOT_FOUND
346
+ throw
347
+ }
348
+
349
+ this.NotifyAssocChanged
350
+ }
351
+
352
+ GetHashesForVersions(versions) {
353
+ versions := ',' versions ','
354
+ files := Map(), files.CaseSense := "off"
355
+ for , cfiles in this.GetComponents(v => InStr(versions, ',' v ','))
356
+ for fh in cfiles
357
+ files[fh.Path] := fh
358
+ return files
359
+ }
360
+
361
+ Uninstall(versions:='') {
362
+ this.ResolveInstallDir
363
+
364
+ this.ElevateIfNeeded
365
+
366
+ files := versions = '' ? this.Hashes.Clone() : this.GetHashesForVersions(versions)
367
+ if !files.Count && versions = ''
368
+ this.GetConfirmation("Installation data missing. Files will not be deleted.", 'x')
369
+
370
+ ; Close scripts and help files
371
+ this.PreUninstallChecks files
372
+
373
+ ; Remove registry key and certificate only if being fully uninstalled
374
+ if versions = '' {
375
+ this.UninstallRegistry
376
+ try EnableUIAccess_DeleteCertAndKey("AutoHotkey")
377
+ }
378
+
379
+ ; Remove files
380
+ SetWorkingDir this.InstallDir
381
+ modified := ""
382
+ dirs := ""
383
+ for path, f in files {
384
+ if !FileExist(path)
385
+ continue
386
+ if HashFile(path) != f.Hash {
387
+ modified .= "`n" path
388
+ continue
389
+ }
390
+ if this.InstallDir '\' path = A_AhkPath {
391
+ postponed := A_AhkPath
392
+ this.Hashes.Delete(path)
393
+ continue
394
+ }
395
+ FileDelete path
396
+ this.Hashes.Delete(path)
397
+ SplitPath path,, &dir
398
+ if dir != ""
399
+ dirs .= dir "`n"
400
+ }
401
+ if modified != "" {
402
+ this.InfoBox("The following files were not deleted as they appear to have been modified:"
403
+ . modified)
404
+ }
405
+
406
+ ; Update or remove hashes file
407
+ if this.Hashes.Count
408
+ this.WriteHashes
409
+ else
410
+ try FileDelete this.HashesPath
411
+
412
+ ; Remove empty directories
413
+ for dir in StrSplit(Sort(RTrim(dirs, "`n"), 'UR'), "`n") {
414
+ this.DeleteLink dir '\AutoHotkey.exe'
415
+ try DirDelete dir, false
416
+ }
417
+
418
+ if versions = '' ; Full uninstall
419
+ this.DeleteLink this.InstallDir '\v2'
420
+
421
+ if IsSet(postponed) {
422
+ ; Try delete via cmd.exe after we exit
423
+ Run(A_ComSpec ' /c "'
424
+ 'ping -n 2 127.1 > NUL & '
425
+ 'del "' postponed '" & '
426
+ 'cd %TEMP% & '
427
+ 'rmdir "' postponed '\.." & '
428
+ 'rmdir "' A_WorkingDir '"'
429
+ '"',, 'Hide')
430
+ }
431
+ ExitApp
432
+ }
433
+
434
+ ;}
435
+
436
+ ;{ Conflict prevention
437
+
438
+ PreApplyChecks() {
439
+ ; Check for files that might be overwritten
440
+ writeFiles := Map(), writeFiles.CaseSense := 'off'
441
+ hasChm := false
442
+ unknownFiles := ''
443
+ modifiedFiles := ''
444
+ hashes := this.Hashes
445
+ if (item := hashes.Get(this.InstallDir '\UX\AutoHotkeyUX.exe', false)) ; Erroneous entry by v2.0-beta.4
446
+ hashes.Delete(item.Path), hashes[item.Path := 'UX\AutoHotkeyUX.exe'] := item ; Make it relative
447
+ for item in this.FileItems {
448
+ if !(attrib := FileExist(item.Dest))
449
+ continue
450
+ if InStr(attrib, 'D')
451
+ this.FatalError("The following file cannot be installed because a directory by this name already exists:`n"
452
+ item.Dest "`n`nNo changes have been made.")
453
+ SplitPath item.Dest,, &destDir, &ext, &destName
454
+ if destDir = 'v2' && this.GetLinkAttrib(destDir)
455
+ continue ; Symlink should be deleted, so item.Dest won't exist.
456
+ if ext = 'exe'
457
+ writeFiles[this.InstallDir '\' item.Dest] := true
458
+ else if ext = 'chm'
459
+ hasChm := true
460
+ if !(curFile := hashes.Get(item.Dest, ''))
461
+ unknownFiles .= item.Dest "`n"
462
+ else if destDir = 'v2' && curFile.Version && curFile.Version != this.Version {
463
+ this.AddPreAction this.DisplaceFile.Bind(, item.Dest
464
+ , 'v' curFile.Version '\' destName '.' ext, curFile.Version)
465
+ if ext = 'exe' && FileExist('v2\' destName '_UIA.exe')
466
+ this.AddPreAction this.DisplaceFile.Bind(, 'v2\' destName '_UIA.exe'
467
+ , 'v' curFile.Version '\' destName '_UIA.exe', '')
468
+ }
469
+ else if curFile.Hash != HashFile(item.Dest)
470
+ modifiedFiles .= item.Dest "`n"
471
+ }
472
+
473
+ ; Find any scripts being executed by files that will be overwritten
474
+ if writeFiles.Has(this.InstallDir '\AutoHotkeyU32.exe') ; Rough check for v1 upgrade
475
+ writeFiles[this.InstallDir '\AutoHotkey.exe'] := true
476
+ ours(exe) => writeFiles.Has(exe) || writeFiles.Has(StrReplace(exe, '_UIA'))
477
+ scripts := this.ScriptsUsingOurFiles(ours)
478
+
479
+ ; Show confirmation prompt
480
+ message := ""
481
+ if scripts.Length {
482
+ message .= "The following scripts will be closed automatically:`n"
483
+ for w in scripts
484
+ message .= this.ScriptTitle(w) "`n"
485
+ message .= "`n"
486
+ }
487
+ if unknownFiles != '' {
488
+ message .= "The following files not created by setup will be overwritten:`n"
489
+ . unknownFiles
490
+ message .= "`n"
491
+ }
492
+ if modifiedFiles != '' {
493
+ message .= "The following files appear to contain modifications that will be lost:`n"
494
+ . modifiedFiles
495
+ message .= "`n"
496
+ }
497
+ if message != ''
498
+ this.GetConfirmation(message)
499
+
500
+ this.CloseScriptsUsingOurFiles(scripts, ours)
501
+ }
502
+
503
+ PreUninstallChecks(files) {
504
+ ours(exe) => files.Has(this.RelativePath(exe))
505
+ scripts := this.ScriptsUsingOurFiles(ours)
506
+ this.CloseScriptsUsingOurFiles(scripts, ours)
507
+ }
508
+
509
+ CloseScriptsUsingOurFiles(scripts, ours) {
510
+ ; Close scripts and help files
511
+ static WM_CLOSE := 0x10
512
+ for w in WinGetList("AutoHotkey ahk_class HH Parent")
513
+ try PostMessage WM_CLOSE,,, w
514
+ for w in scripts
515
+ try PostMessage WM_CLOSE,,, w
516
+ ; Wait for windows/scripts to close
517
+ WinWaitClose "AutoHotkey ahk_class HH Parent"
518
+ loop {
519
+ Sleep 100
520
+ ; Refresh the list in case scripts have started/stopped
521
+ scripts := this.ScriptsUsingOurFiles(ours)
522
+ ; Prompt again after around 3 seconds of waiting
523
+ if scripts.Length && Mod(A_Index, 30) = 0 {
524
+ message := "The following scripts must be closed manually before setup can continue:`n"
525
+ for w in scripts
526
+ message .= this.ScriptTitle(w) "`n"
527
+ this.GetConfirmation(message)
528
+ }
529
+ } until scripts.Length = 0
530
+ }
531
+
532
+ ScriptsUsingOurFiles(ours) {
533
+ scripts := [], dhw := A_DetectHiddenWindows
534
+ DetectHiddenWindows true
535
+ for w in WinGetList('ahk_class AutoHotkey') {
536
+ if w = A_ScriptHwnd
537
+ continue
538
+ if ours(WinGetProcessPath(w))
539
+ scripts.Push(w)
540
+ }
541
+ DetectHiddenWindows dhw
542
+ return scripts
543
+ }
544
+
545
+ ScriptTitle(wnd) {
546
+ try
547
+ return RegExReplace(WinGetTitle(wnd), ' - AutoHotkey v.*')
548
+ catch
549
+ return "(unable to retrieve title - already exited?)"
550
+ }
551
+
552
+ ;}
553
+
554
+ ;{ Components to install
555
+
556
+ AddCoreFiles(destSubDir) {
557
+ this.AddFiles(this.SourceDir, destSubDir
558
+ , 'AutoHotkey*.exe', 'AutoHotkey.chm'
559
+ )
560
+ this.AddFiles(this.SourceDir, destSubDir = '.' ? 'Compiler' : destSubDir
561
+ , 'Compiler\*.bin' ; Legacy base files for compiler - even if Ahk2Exe is not installed yet.
562
+ )
563
+
564
+ ; Queue creation of UIA executable files
565
+ if A_IsAdmin && this.IsTrustedLocation(this.InstallDir) && VerCompare(this.Version, '1.1.19') >= 0
566
+ Loop Files this.SourceDir '\AutoHotkey*.exe'
567
+ this.AddPostAction this.MakeUIA.Bind(, destSubDir '\' A_LoopFileName)
568
+ }
569
+
570
+ AddMiscFiles() {
571
+ this.AddFiles(this.SourceDir, '.', 'license.txt')
572
+ }
573
+
574
+ AddCompiler(compilerSourceDir) {
575
+ this.AddFiles(compilerSourceDir, 'Compiler', 'Ahk2Exe.exe')
576
+ this.AddVerb('Compile', 'Compiler\Ahk2Exe.exe', '/in "%l" %*', "Compile script")
577
+ this.AddVerb('Compile-Gui', 'Compiler\Ahk2Exe.exe', '/gui /in "%l" %*', "Compile script (GUI)...")
578
+ this.AddPostAction this.CreateCompilerShortcut
579
+ }
580
+
581
+ AddUXFiles() {
582
+ this.AddFiles(this.SourceDir '\UX', 'UX', '*.ahk')
583
+ this.AddFiles(this.SourceDir '\UX', 'UX\inc', 'inc\*')
584
+ this.AddFiles(this.SourceDir '\UX\Templates', 'UX\Templates', '*.ahk')
585
+ this.AddPostAction this.CreateStartShortcut
586
+ }
587
+
588
+ AddSoftwareReg() {
589
+ this.AddRegValues(this.SoftwareKey, [
590
+ {ValueName: 'InstallDir', Value: this.InstallDir},
591
+ {ValueName: 'InstallCommand', Value: this.CmdStr('UX\install.ahk', '/install "%1"')},
592
+ {ValueName: 'Version', Value: this.Version},
593
+ ])
594
+ }
595
+
596
+ AddUninstallReg() {
597
+ this.AddRegValues(this.UninstallKey, [
598
+ {ValueName: 'DisplayName', Value: this.ProductName (this.RootKey = 'HKCU' ? " (user)" : "")},
599
+ {ValueName: 'UninstallString', Value: this.UninstallCmd},
600
+ {ValueName: 'QuietUninstallString', Value: this.QUninstallCmd},
601
+ {ValueName: 'NoModify', Value: 1},
602
+ {ValueName: 'DisplayIcon', Value: this.Interpreter},
603
+ {ValueName: 'DisplayVersion', Value: this.Version},
604
+ {ValueName: 'URLInfoAbout', Value: this.ProductURL},
605
+ {ValueName: 'Publisher', Value: this.Publisher},
606
+ {ValueName: 'InstallLocation', Value: this.InstallDir},
607
+ ])
608
+
609
+ }
610
+
611
+ AddFileTypeReg() {
612
+ this.AddRegValues(this.ClassesKey, [
613
+ {Key: '.ahk', Value: this.ScriptProgId},
614
+ {Key: '.ahk\ShellNew', ValueName: 'Command', Value: this.CmdStr('UX\ui-newscript.ahk', '"%1"')},
615
+ {Key: '.ahk\ShellNew', ValueName: 'FileName'},
616
+ {Key: '.ahk\PersistentHandler', Value: '{5e941d80-bf96-11cd-b579-08002b30bfeb}'}
617
+ ])
618
+ this.AddRegValues(this.FileTypeKey, [
619
+ {Value: "AutoHotkey Script"},
620
+ {ValueName: 'AppUserModelID', Value: this.AppUserModelID}, ; Testing produced inconsistent results, but it seems sometimes this must be here, sometimes under the verb.
621
+ {Key: 'DefaultIcon', Value: this.Interpreter ",1"},
622
+ {Key: 'Shell', Value: 'Open runas UIAccess Edit'}, ; Including 'runas' in lower-case fixes the shield icon not appearing on Windows 11.
623
+ {Key: 'Shell\Open', ValueName: 'FriendlyAppName', Value: 'AutoHotkey Launcher'},
624
+ ])
625
+ this.AddRunVerbs()
626
+ this.AddEditVerbIfUnset()
627
+ this.AddPostAction this.NotifyAssocChanged
628
+ }
629
+
630
+ AddRunVerbs() {
631
+ aumid := {ValueName: 'AppUserModelID', Value: this.AppUserModelID}
632
+ this.AddVerb('Open', 'UX\launcher.ahk', '"%1" %*', "Run script",
633
+ aumid
634
+ )
635
+ this.AddVerb('RunAs', 'UX\launcher.ahk', '"%1" %*',
636
+ aumid, {ValueName: 'HasLUAShield', Value: ""}
637
+ )
638
+ if A_IsAdmin && this.IsTrustedLocation(this.InstallDir) {
639
+ this.AddVerb('UIAccess', 'UX\launcher.ahk', '/runwith UIA "%1" %*',
640
+ "Run with UI access", aumid)
641
+ }
642
+ ; Add *Launch as a hidden verb, accessible via Run('*Launch ' file).
643
+ this.AddVerb('Launch', 'UX\launcher.ahk', '/Launch "%1" %*', "Launch",
644
+ aumid, {ValueName: 'ProgrammaticAccessOnly', Value: ""}
645
+ )
646
+ }
647
+
648
+ AddEditVerbIfUnset() {
649
+ static v1_edit_cmd := 'notepad.exe %1'
650
+ ; Add edit verb only if it is undefined or has its default v1 value.
651
+ if RegRead(this.FileTypeKey '\Shell\Edit\Command',, v1_edit_cmd) = v1_edit_cmd
652
+ this.AddVerb('Edit', 'UX\ui-editor.ahk', '"%1"', "Edit script")
653
+ }
654
+
655
+ ;}
656
+
657
+ ;{ Utility functions
658
+
659
+ RelativePath(p) => (
660
+ i := this.InstallDir '\',
661
+ SubStr(p, 1, StrLen(i)) = i ? SubStr(p, StrLen(i) + 1) : p
662
+ )
663
+
664
+ CmdStr(script, args:='')
665
+ => RTrim(Format((InStr(script, '.ahk') ? '"{1}" ' : '') '"{2}\{3}" {4}'
666
+ , this.Interpreter, this.InstallDir, script, args))
667
+
668
+ AddRegValues(key, values) {
669
+ for v in values {
670
+ i := {}
671
+ i.Key := key (v.HasProp('Key') ? '\' v.Key : '')
672
+ i.ValueName := v.HasProp('ValueName') ? v.ValueName : ''
673
+ (v is Primitive) ? i.Value := v :
674
+ (v.HasProp('Value')) ? i.Value := v.Value : 0
675
+ this.RegItems.Push(i)
676
+ }
677
+ }
678
+
679
+ AddVerb(name, script, args, values*) {
680
+ this.AddRegValues(this.FileTypeKey '\Shell\' name, [
681
+ {Key: 'Command', Value: this.CmdStr(script, args)},
682
+ values*
683
+ ])
684
+ }
685
+
686
+ AddFileCopy(sourcePath, destPath) {
687
+ this.FileItems.Push {Source: sourcePath, Dest: destPath}
688
+ }
689
+
690
+ AddFiles(sourceDir, destSubDir, patterns*) {
691
+ destSubDir := (destSubDir != '.' ? destSubDir '\' : '')
692
+ for p in patterns {
693
+ Loop Files sourceDir '\' p {
694
+ this.AddFileCopy A_LoopFileFullPath, destSubDir . A_LoopFileName
695
+ }
696
+ }
697
+ }
698
+
699
+ AddPreCheck(f) => this.PreCheck.Push(f)
700
+ AddPreAction(f) => this.PreAction.Push(f)
701
+ AddPostAction(f) => this.PostAction.Push(f)
702
+
703
+ ReadHashes() {
704
+ ; For maintainability, don't assume the caller has set the working dir.
705
+ wd := A_WorkingDir, A_WorkingDir := this.InstallDir
706
+ hashes := ReadHashes(this.HashesPath, item => FileExist(item.Path))
707
+ A_WorkingDir := wd
708
+ return hashes
709
+ }
710
+
711
+ AddFileHash(f, v) {
712
+ this.Hashes[f] := {Path: f, Hash: HashFile(f), Version: v}
713
+ }
714
+
715
+ WriteHashes() {
716
+ s := "Hash,Version,Path,Description`r`n"
717
+ for ,item in this.Hashes {
718
+ if !item.HasProp('Description') {
719
+ try ; Cache the file description for the launcher
720
+ exe := GetExeInfo(item.Path)
721
+ catch
722
+ item.Description := ""
723
+ else {
724
+ item.Description := exe.Description
725
+ if InStr(item.Description, 'AutoHotkey')
726
+ item.Version := exe.Version ; Ensure accuracy for the launcher
727
+ }
728
+ }
729
+ s .= Format('{},{},"{}","{}"`r`n', item.Hash, item.Version, item.Path, item.Description)
730
+ }
731
+ FileOpen(this.HashesPath, 'w').Write(s)
732
+ }
733
+
734
+ GetComponents(versionFilter?) {
735
+ callerwd := A_WorkingDir
736
+ SetWorkingDir this.InstallDir
737
+ versions := Map()
738
+ maxes := Map()
739
+ for , fh in this.Hashes {
740
+ if fh.Path ~= 'i)^UX\\|^[A-Z]:|^\\\\|^(WindowSpy\.ahk|license\.txt)$'
741
+ . '|^Compiler\\(?!.*\.bin$)'
742
+ continue
743
+ try fh.Version := GetExeInfo(fh.Path = 'AutoHotkey.chm' ? 'AutoHotkeyU32.exe' : fh.Path).Version ; Auto-fix inaccurate versions in Hashes
744
+ if !files := versions.Get(fh.Version, 0) {
745
+ if IsSet(versionFilter) && !versionFilter(fh.Version)
746
+ continue
747
+ versions[fh.Version] := files := []
748
+ v := RegExReplace(fh.Version, '^\d+\.\d+\b\K.*')
749
+ if VerCompare(prevMax := maxes.Get(v, ''), fh.Version) < 0 {
750
+ maxes[v] := fh.Version
751
+ if prevMax != ''
752
+ versions[prevMax].superseded := true
753
+ }
754
+ else
755
+ files.superseded := true
756
+ }
757
+ files.InsertAt(1, fh)
758
+ }
759
+ SetWorkingDir callerwd
760
+ return versions
761
+ }
762
+
763
+ NotifyAssocChanged() {
764
+ DllCall("shell32\SHChangeNotify", "uint", 0x08000000 ; SHCNE_ASSOCCHANGED
765
+ , "uint", 0, "ptr", 0, "ptr", 0)
766
+ }
767
+
768
+ GetConfirmation(message, icon:='!') {
769
+ if !this.Silent && MsgBox(message, this.DialogTitle, 'Icon' icon ' OkCancel') = 'Cancel'
770
+ ExitApp 1
771
+ }
772
+
773
+ WarnBox(message) {
774
+ if !this.Silent
775
+ MsgBox message, this.DialogTitle, "Icon!"
776
+ }
777
+
778
+ InfoBox(message) {
779
+ if !this.Silent
780
+ MsgBox message, this.DialogTitle, "Iconi"
781
+ }
782
+
783
+ FatalError(message) {
784
+ if !this.Silent
785
+ MsgBox message, this.DialogTitle, 'Iconx'
786
+ ExitApp 1
787
+ }
788
+
789
+ ErrorBox(err, abortText?) { ; Show a standard error dialog with stack trace etc.
790
+ OnMessage(0xBAAD, newthread, -2),
791
+ PostMessage(0xBAAD, 0, 0, A_ScriptHwnd),
792
+ SendMessage(0xBAAD, ObjPtr(err), 0, A_ScriptHwnd),
793
+ OnMessage(0xBAAD, newthread, 0)
794
+ newthread(ep, *) {
795
+ if ep
796
+ throw ObjFromPtrAddRef(ep)
797
+ if ep ; This line prevents an "unreachable" warning in v2.0.
798
+ return 0 ; This line prevents continuation of the function in v2.1-alpha.3+ (if user selects Continue).
799
+ DetectHiddenWindows true
800
+ WinExist "ahk_class #32770 ahk_pid " ProcessExist()
801
+ Loop 5
802
+ ControlHide "Button" A_Index+1
803
+ ControlSetText abortText, "Button1"
804
+ return 0
805
+ }
806
+ }
807
+
808
+ GetTargetUX() {
809
+ try {
810
+ ; For registered installations, InstallCommand allows for future changes.
811
+ return {
812
+ Version: RegRead(this.SoftwareKey, 'Version'),
813
+ InstallCommand: RegRead(this.SoftwareKey, 'InstallCommand')
814
+ }
815
+ }
816
+ try {
817
+ ; Target installation not in registry, or has no InstallCommand (e.g. too old).
818
+ ; Allow non-registry installations that follow protocol as commented below.
819
+ ux := {}
820
+ ; Version information must be provided by the file at this.HashesPath:
821
+ ux.Version := this.Hashes['UX\install.ahk'].Version
822
+ ; Interpreter must be located at the path calculated below:
823
+ interpreter := this.InstallDir '\UX\AutoHotkeyUX.exe'
824
+ if FileExist(interpreter) {
825
+ ; Additional interpreters must be installable with this command line:
826
+ ux.InstallCommand := Format('"{1}" "{2}\UX\install.ahk" /install "%1"'
827
+ , interpreter, this.InstallDir)
828
+ return ux
829
+ }
830
+ }
831
+ ; Otherwise, UX script or appropriate interpreter not found.
832
+ }
833
+
834
+ ; Delete a symbolic link, or do nothing if path does not refer to a symbolic link.
835
+ DeleteLink(path) {
836
+ switch this.GetLinkAttrib(path) {
837
+ case 'D': DirDelete path
838
+ case 'F': FileDelete path
839
+ }
840
+ }
841
+
842
+ GetLinkAttrib(path) {
843
+ attrib := DllCall('GetFileAttributes', 'str', path)
844
+ ; FILE_ATTRIBUTE_REPARSE_POINT = 0x400
845
+ ; FILE_ATTRIBUTE_DIRECTORY = 0x10
846
+ return (attrib != -1 && (attrib & 0x400)) ? ((attrib & 0x10) ? 'D' : 'F') : ''
847
+ }
848
+
849
+ UpdateV2Link() {
850
+ ; Create a symlink for AutoHotkey.exe to simplify use by tools.
851
+ DllCall('CreateSymbolicLink', 'str', this.InstallDir '\v2\AutoHotkey.exe'
852
+ , 'str', 'AutoHotkey' (A_Is64bitOS ? '64' : '32') '.exe', 'uint', 0)
853
+ }
854
+
855
+ CreateWindowSpyRedirect() {
856
+ ; Permit overwrite only when upgrading a legacy v1 installation,
857
+ ; or if it is known to have been created by us.
858
+ if !FileExist('WindowSpy.ahk') || this.HasOwnProp('SoftwareKeyV1')
859
+ || this.Hashes.Get('WindowSpy.ahk', {Hash: ''}).Hash = HashFile('WindowSpy.ahk') {
860
+ FileOpen('WindowSpy.ahk', 'w').Write('
861
+ (
862
+ #include UX
863
+ #include inc\bounce-v1.ahk
864
+ /**/
865
+ #requires AutoHotkey v2.0
866
+ try Run('"' A_MyDocuments '\AutoHotkey\WindowSpy.ahk"'), ExitApp()
867
+ #include WindowSpy.ahk
868
+ )')
869
+ this.AddFileHash('WindowSpy.ahk', this.Version)
870
+ }
871
+ }
872
+
873
+ CreateStartShortcut() {
874
+ if this.Hashes.Has(this.StartFolder '\AutoHotkey.lnk')
875
+ try FileDelete this.StartFolder '\AutoHotkey.lnk'
876
+ CreateAppShortcut(
877
+ lnk := this.StartFolder '\AutoHotkey Dash.lnk', {
878
+ target: this.Interpreter,
879
+ args: Format('"{1}\UX\ui-dash.ahk"', this.InstallDir),
880
+ desc: "AutoHotkey Dash",
881
+ aumid: this.AppUserModelID,
882
+ uninst: this.UninstallCmd
883
+ }
884
+ )
885
+ this.AddFileHash lnk, this.Version
886
+
887
+ CreateAppShortcut(
888
+ lnk := this.StartFolder '\AutoHotkey Window Spy.lnk', {
889
+ target: this.Interpreter,
890
+ args: Format('"{1}\UX\WindowSpy.ahk"', this.InstallDir),
891
+ desc: "AutoHotkey Window Spy",
892
+ aumid: 'AutoHotkey.WindowSpy',
893
+ icon: Format('{1}\UX\inc\spy.ico', this.InstallDir), iconIndex: 0,
894
+ uninst: this.UninstallCmd
895
+ }
896
+ )
897
+ this.AddFileHash lnk, this.Version
898
+ }
899
+
900
+ CreateCompilerShortcut() {
901
+ CreateAppShortcut(
902
+ lnk := this.StartFolder '\Ahk2Exe.lnk', {
903
+ target: this.InstallDir '\Compiler\Ahk2Exe.exe',
904
+ desc: "Convert .ahk to .exe",
905
+ aumid: 'AutoHotkey.Ahk2Exe',
906
+ uninst: this.UninstallCmd
907
+ }
908
+ )
909
+ this.AddFileHash lnk, this.Version
910
+ }
911
+
912
+ MakeUIA(baseFile) {
913
+ SplitPath baseFile,, &baseDir,, &baseName
914
+ baseDir := baseDir = '.' ? '' : baseDir '\'
915
+ newPath := baseDir baseName '_UIA.exe'
916
+ static abort := false ; Let "Abort" disable MakeUIA calls, but let other PostActions complete.
917
+ while !abort {
918
+ FileCopy baseFile, newPath, true
919
+ try {
920
+ EnableUIAccess newPath
921
+ break
922
+ }
923
+ catch as e {
924
+ try FileDelete newPath
925
+ if e.What != "EndUpdateResource" {
926
+ abort := true
927
+ this.WarnBox "An error occurred while generating files to support the `"Run with UI access`" option. An error dialog will be shown with additional information, then setup will continue."
928
+ throw e
929
+ }
930
+ if this.Silent {
931
+ if A_Index > 4
932
+ break
933
+ Sleep 500
934
+ }
935
+ switch MsgBox("Unable to create " baseName "_UIA.exe. Try adding an exclusion in your antivirus software. If that doesn't work, please report the issue.`n`nError: " e.Message
936
+ , this.DialogTitle, "Icon! a/r/i") {
937
+ case "Abort": abort := true
938
+ case "Ignore": break
939
+ }
940
+ }
941
+ }
942
+ if FileExist(newPath)
943
+ this.AddFileHash newPath, '' ; For uninstall
944
+ }
945
+
946
+ IsTrustedLocation(path) { ; http://msdn.com/library/bb756929
947
+ other := EnvGet(A_PtrSize=8 ? "ProgramFiles(x86)" : "ProgramW6432")
948
+ return InStr(path, A_ProgramFiles "\") = 1
949
+ || other && InStr(path, other "\") = 1
950
+ }
951
+
952
+ ;}
953
+
954
+ ;{ Upgrade from v1
955
+
956
+ PrepareUpgradeV1(installedVersion) {
957
+ ; This needs to be done before conflict-checking
958
+ if FileExist('license.txt')
959
+ this.AddFileHash('license.txt', installedVersion)
960
+ }
961
+
962
+ UpgradeV1(installedVersion) {
963
+ try { ; Permit failure in case AutoHotkey.exe has been deleted.
964
+ exe := GetExeInfo('AutoHotkey.exe')
965
+ build := RegExReplace(exe.Description, '^AutoHotkey *')
966
+ }
967
+
968
+ ; Set default launcher settings
969
+ if IsSet(build) && ConfigRead('Launcher\v1', 'Build', '!') = '!'
970
+ ConfigWrite(build, 'Launcher\v1', 'Build')
971
+ if ConfigRead('Launcher\v1', 'UTF8', '') = ''
972
+ && InStr(RegRead('HKCR\' this.ScriptProgId '\Shell\Open\Command',, ''), '/cp65001 ')
973
+ ConfigWrite(true, 'Launcher\v1', 'UTF8')
974
+
975
+ if FileExist('Compiler\Ahk2Exe.exe')
976
+ this.CreateCompilerShortcut
977
+
978
+ ; Record these for Uninstall
979
+ add 'AutoHotkey{1}.exe', '', 'A32', 'U32', 'U64', 'A32_UIA', 'U32_UIA', 'U64_UIA'
980
+ add 'Compiler\{1}.bin', 'ANSI 32-bit', 'Unicode 32-bit', 'Unicode 64-bit', 'AutoHotkeySC'
981
+ add '{1}', 'Compiler\Ahk2Exe.exe', 'AutoHotkey.chm', A_WinDir '\ShellNew\Template.ahk'
982
+
983
+ add(fmt, patterns*) {
984
+ for p in patterns
985
+ if FileExist(f := Format(fmt, p))
986
+ this.AddFileHash f, installedVersion
987
+ }
988
+
989
+ ; Remove obsolete files
990
+ for item in ['Installer.ahk', 'AutoHotkey Website.url']
991
+ try FileDelete item
992
+
993
+ ; Remove the v1 shortcuts from the Start menu
994
+ name := RegRead(this.SoftwareKeyV1, 'StartMenuFolder', '')
995
+ if name != ''
996
+ try DirDelete A_ProgramsCommon '\' name, true
997
+
998
+ ; Remove the old sub-keys, which might be in the wrong reg view
999
+ try RegDeleteKey this.SoftwareKeyV1
1000
+ try RegDeleteKey this.UninstallKeyV1
1001
+ }
1002
+
1003
+ DisplaceFile(sourcePath, destPath, version) {
1004
+ SplitPath destPath,, &destDir
1005
+ if destDir != ""
1006
+ DirCreate destDir
1007
+ try
1008
+ FileMove sourcePath, destPath
1009
+ catch as e
1010
+ throw (e.Message := OSError().Message "`nSource: " sourcePath "`nDestination: " destPath, e.Extra := "", e)
1011
+ try this.Hashes.Delete(sourcePath)
1012
+ this.AddFileHash destPath, version
1013
+ }
1014
+
1015
+ DisplaceV1(v) {
1016
+ DirCreate dir := 'v' v
1017
+ displace(path) {
1018
+ if FileExist(path) {
1019
+ SplitPath path, &name
1020
+ this.DisplaceFile path, dir '\' name, v
1021
+ }
1022
+ }
1023
+ for build in ['U32', 'U64', 'A32'] {
1024
+ displace 'AutoHotkey' build '.exe'
1025
+ displace 'AutoHotkey' build '_UIA.exe'
1026
+ }
1027
+ try
1028
+ defaultBinSize := FileGetSize(defaultBinPath := 'Compiler\AutoHotkeySC.bin')
1029
+ for build in ['Unicode 32-bit', 'Unicode 64-bit', 'ANSI 32-bit'] {
1030
+ try
1031
+ if FileGetSize('Compiler\' build '.bin') = defaultBinSize
1032
+ this.AddFileCopy this.InstallDir '\Compiler\' build '.bin', defaultBinPath
1033
+ displace 'Compiler\' build '.bin'
1034
+ }
1035
+
1036
+ displace 'AutoHotkey.chm'
1037
+ try {
1038
+ exe := GetExeInfo('AutoHotkey.exe')
1039
+ if exe.Version = v
1040
+ && RegExMatch(exe.Description, ' (A|U)\w+ (32|64)-bit$', &m) {
1041
+ ; Too early to add to FileItems, so use PostAction:
1042
+ this.AddPostAction this.CopyDefaultExe.Bind(, 'AutoHotkey' m.1 m.2 '.exe')
1043
+ }
1044
+ }
1045
+ }
1046
+
1047
+ CopyDefaultExe(from) {
1048
+ try
1049
+ FileCopy from, 'AutoHotkey.exe', true
1050
+ catch
1051
+ return ; TODO: report to user?
1052
+ this.AddFileHash 'AutoHotkey.exe', this.Version
1053
+ }
1054
+
1055
+ ;}
1056
+ }