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.
- package/.editorconfig +9 -0
- package/.eslintignore +3 -0
- package/.eslintrc.js +55 -0
- package/.jsdoc +25 -0
- package/AutoHotkey_2.0.19/AutoHotkey.chm +0 -0
- package/AutoHotkey_2.0.19/AutoHotkey32.exe +0 -0
- package/AutoHotkey_2.0.19/AutoHotkey64.exe +0 -0
- package/AutoHotkey_2.0.19/Install.cmd +2 -0
- package/AutoHotkey_2.0.19/UX/Templates/Minimal for v2.ahk +6 -0
- package/AutoHotkey_2.0.19/UX/WindowSpy.ahk +246 -0
- package/AutoHotkey_2.0.19/UX/inc/CommandLineToArgs.ahk +12 -0
- package/AutoHotkey_2.0.19/UX/inc/CreateAppShortcut.ahk +37 -0
- package/AutoHotkey_2.0.19/UX/inc/EnableUIAccess.ahk +246 -0
- package/AutoHotkey_2.0.19/UX/inc/GetGitHubReleaseAssetURL.ahk +26 -0
- package/AutoHotkey_2.0.19/UX/inc/HashFile.ahk +96 -0
- package/AutoHotkey_2.0.19/UX/inc/README.txt +3 -0
- package/AutoHotkey_2.0.19/UX/inc/ShellRun.ahk +7 -0
- package/AutoHotkey_2.0.19/UX/inc/bounce-v1.ahk +3 -0
- package/AutoHotkey_2.0.19/UX/inc/common.ahk +21 -0
- package/AutoHotkey_2.0.19/UX/inc/config.ahk +13 -0
- package/AutoHotkey_2.0.19/UX/inc/identify.ahk +29 -0
- package/AutoHotkey_2.0.19/UX/inc/identify_regex.ahk +4 -0
- package/AutoHotkey_2.0.19/UX/inc/launcher-common.ahk +78 -0
- package/AutoHotkey_2.0.19/UX/inc/spy.ico +0 -0
- package/AutoHotkey_2.0.19/UX/inc/ui-base.ahk +129 -0
- package/AutoHotkey_2.0.19/UX/install-ahk2exe.ahk +53 -0
- package/AutoHotkey_2.0.19/UX/install-version.ahk +109 -0
- package/AutoHotkey_2.0.19/UX/install.ahk +1056 -0
- package/AutoHotkey_2.0.19/UX/launcher.ahk +430 -0
- package/AutoHotkey_2.0.19/UX/reload-v1.ahk +22 -0
- package/AutoHotkey_2.0.19/UX/reset-assoc.ahk +38 -0
- package/AutoHotkey_2.0.19/UX/ui-dash.ahk +200 -0
- package/AutoHotkey_2.0.19/UX/ui-editor.ahk +238 -0
- package/AutoHotkey_2.0.19/UX/ui-launcherconfig.ahk +195 -0
- package/AutoHotkey_2.0.19/UX/ui-newscript.ahk +296 -0
- package/AutoHotkey_2.0.19/UX/ui-setup.ahk +175 -0
- package/AutoHotkey_2.0.19/UX/ui-uninstall.ahk +68 -0
- package/AutoHotkey_2.0.19/WindowSpy.ahk +2 -0
- package/AutoHotkey_2.0.19/license.txt +351 -0
- package/AutoHotkey_2.0.19/zclick.ahk +5 -0
- package/AutoHotkey_2.0.19/zrun_click.bat +2 -0
- package/AutoHotkey_2.0.19/zrun_send.bat +2 -0
- package/AutoHotkey_2.0.19/zsend.ahk +4 -0
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/SECURITY.md +5 -0
- package/_tmp/view.all.png +0 -0
- package/_tmp/view.det.png +0 -0
- package/babel.config.js +16 -0
- package/dist/action.umd.js +32 -0
- package/dist/action.umd.js.map +1 -0
- package/dist/ck-pic.umd.js +7 -0
- package/dist/ck-pic.umd.js.map +1 -0
- package/dist/compare.umd.js +7 -0
- package/dist/compare.umd.js.map +1 -0
- package/dist/get-settings.umd.js +7 -0
- package/dist/get-settings.umd.js.map +1 -0
- package/dist/mousekey.umd.js +7 -0
- package/dist/mousekey.umd.js.map +1 -0
- package/dist/screen.umd.js +7 -0
- package/dist/screen.umd.js.map +1 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/index.html +84 -0
- package/docs/scripts/collapse.js +39 -0
- package/docs/scripts/commonNav.js +28 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/nav.js +12 -0
- package/docs/scripts/polyfill.js +4 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/scripts/search.js +99 -0
- package/docs/styles/jsdoc.css +776 -0
- package/docs/styles/prettify.css +80 -0
- package/g.mjs +46 -0
- package/g.test_opencv.mjs +36 -0
- package/package.json +41 -0
- package/pic/view.png +0 -0
- package/script.txt +17 -0
- package/src/action.mjs +132 -0
- package/src/ckPic.mjs +95 -0
- package/src/compare.mjs +201 -0
- package/src/getSettings.mjs +17 -0
- package/src/mousekey.mjs +95 -0
- package/src/screen.mjs +62 -0
- package/test/WMousekey.test.mjs +11 -0
- package/toolg/addVersion.mjs +4 -0
- package/toolg/cleanFolder.mjs +4 -0
- package/toolg/gDistRollup.mjs +33 -0
- 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
|
+
}
|