Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History
338 lines (274 loc) · 14 KB

File metadata and controls

338 lines (274 loc) · 14 KB
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32;
using NuGet;
using Squirrel.SimpleSplat;
using Squirrel.Shell;
namespace Squirrel
{
public sealed partial class UpdateManager : IUpdateManager, IEnableLogger
{
readonly string rootAppDirectory;
readonly string applicationName;
readonly IFileDownloader urlDownloader;
readonly string updateUrlOrPath;
IDisposable updateLock;
public UpdateManager(string urlOrPath,
string applicationName = null,
string rootDirectory = null,
IFileDownloader urlDownloader = null)
{
Contract.Requires(!String.IsNullOrEmpty(urlOrPath));
Contract.Requires(!String.IsNullOrEmpty(applicationName));
updateUrlOrPath = urlOrPath;
this.applicationName = applicationName ?? UpdateManager.getApplicationName();
this.urlDownloader = urlDownloader ?? new FileDownloader();
if (rootDirectory != null) {
this.rootAppDirectory = Path.Combine(rootDirectory, this.applicationName);
return;
}
this.rootAppDirectory = Path.Combine(rootDirectory ?? GetLocalAppDataDirectory(), this.applicationName);
}
public async Task<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates = false, Action<int> progress = null, UpdaterIntention intention = UpdaterIntention.Update)
{
var checkForUpdate = new CheckForUpdateImpl(rootAppDirectory);
await acquireUpdateLock();
return await checkForUpdate.CheckForUpdate(intention, Utility.LocalReleaseFileForAppDir(rootAppDirectory), updateUrlOrPath, ignoreDeltaUpdates, progress, urlDownloader);
}
public async Task DownloadReleases(IEnumerable<ReleaseEntry> releasesToDownload, Action<int> progress = null)
{
var downloadReleases = new DownloadReleasesImpl(rootAppDirectory);
await acquireUpdateLock();
await downloadReleases.DownloadReleases(updateUrlOrPath, releasesToDownload, progress, urlDownloader);
}
public async Task<string> ApplyReleases(UpdateInfo updateInfo, Action<int> progress = null)
{
var applyReleases = new ApplyReleasesImpl(rootAppDirectory);
await acquireUpdateLock();
return await applyReleases.ApplyReleases(updateInfo, false, false, progress);
}
public async Task FullInstall(bool silentInstall = false, Action<int> progress = null)
{
var updateInfo = await CheckForUpdate(intention: UpdaterIntention.Install);
await DownloadReleases(updateInfo.ReleasesToApply);
var applyReleases = new ApplyReleasesImpl(rootAppDirectory);
await acquireUpdateLock();
await applyReleases.ApplyReleases(updateInfo, silentInstall, true, progress);
}
public async Task FullUninstall()
{
var applyReleases = new ApplyReleasesImpl(rootAppDirectory);
await acquireUpdateLock();
this.KillAllExecutablesBelongingToPackage();
await applyReleases.FullUninstall();
}
public Task<RegistryKey> CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch)
{
var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory);
return installHelpers.CreateUninstallerRegistryEntry(uninstallCmd, quietSwitch);
}
public Task<RegistryKey> CreateUninstallerRegistryEntry()
{
var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory);
return installHelpers.CreateUninstallerRegistryEntry();
}
public void RemoveUninstallerRegistryEntry()
{
var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory);
installHelpers.RemoveUninstallerRegistryEntry();
}
public void CreateShortcutsForExecutable(string exeName, ShortcutLocation locations, bool updateOnly, string programArguments = null, string icon = null)
{
var installHelpers = new ApplyReleasesImpl(rootAppDirectory);
installHelpers.CreateShortcutsForExecutable(exeName, locations, updateOnly, programArguments, icon);
}
public Dictionary<ShortcutLocation, ShellLink> GetShortcutsForExecutable(string exeName, ShortcutLocation locations, string programArguments = null)
{
var installHelpers = new ApplyReleasesImpl(rootAppDirectory);
return installHelpers.GetShortcutsForExecutable(exeName, locations, programArguments);
}
public void RemoveShortcutsForExecutable(string exeName, ShortcutLocation locations)
{
var installHelpers = new ApplyReleasesImpl(rootAppDirectory);
installHelpers.RemoveShortcutsForExecutable(exeName, locations);
}
public SemanticVersion CurrentlyInstalledVersion(string executable = null)
{
executable = executable ??
Path.GetDirectoryName(typeof(UpdateManager).Assembly.Location);
if (!executable.StartsWith(rootAppDirectory, StringComparison.OrdinalIgnoreCase)) {
return null;
}
var appDirName = executable.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.FirstOrDefault(x => x.StartsWith("app-", StringComparison.OrdinalIgnoreCase));
if (appDirName == null) return null;
return appDirName.ToSemanticVersion();
}
public void KillAllExecutablesBelongingToPackage()
{
var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory);
installHelpers.KillAllProcessesBelongingToPackage();
}
public string ApplicationName {
get { return applicationName; }
}
public string RootAppDirectory {
get { return rootAppDirectory; }
}
public bool IsInstalledApp {
get { return Assembly.GetExecutingAssembly().Location.StartsWith(RootAppDirectory, StringComparison.OrdinalIgnoreCase); }
}
public void Dispose()
{
var disp = Interlocked.Exchange(ref updateLock, null);
if (disp != null) {
disp.Dispose();
}
}
static bool exiting = false;
public static void RestartApp(string exeToStart = null, string arguments = null)
{
// NB: Here's how this method works:
//
// 1. We're going to pass the *name* of our EXE and the params to
// Update.exe
// 2. Update.exe is going to grab our PID (via getting its parent),
// then wait for us to exit.
// 3. We exit cleanly, dropping any single-instance mutexes or
// whatever.
// 4. Update.exe unblocks, then we launch the app again, possibly
// launching a different version than we started with (this is why
// we take the app's *name* rather than a full path)
exeToStart = exeToStart ?? Path.GetFileName(Assembly.GetEntryAssembly().Location);
var argsArg = arguments != null ?
String.Format("-a \"{0}\"", arguments) : "";
exiting = true;
Process.Start(getUpdateExe(), String.Format("--processStartAndWait \"{0}\" {1}", exeToStart, argsArg));
// NB: We have to give update.exe some time to grab our PID, but
// we can't use WaitForInputIdle because we probably don't have
// whatever WaitForInputIdle considers a message loop.
Thread.Sleep(500);
Environment.Exit(0);
}
public static async Task<Process> RestartAppWhenExited(string exeToStart = null, string arguments = null)
{
// NB: Here's how this method works:
//
// 1. We're going to pass the *name* of our EXE and the params to
// Update.exe
// 2. Update.exe is going to grab our PID (via getting its parent),
// then wait for us to exit.
// 3. Return control and new Process back to caller and allow them to Exit as desired.
// 4. After our process exits, Update.exe unblocks, then we launch the app again, possibly
// launching a different version than we started with (this is why
// we take the app's *name* rather than a full path)
exeToStart = exeToStart ?? Path.GetFileName(Assembly.GetEntryAssembly().Location);
var argsArg = arguments != null ?
String.Format("-a \"{0}\"", arguments) : "";
exiting = true;
var updateProcess = Process.Start(getUpdateExe(), String.Format("--processStartAndWait {0} {1}", exeToStart, argsArg));
await Task.Delay(500);
return updateProcess;
}
public static string GetLocalAppDataDirectory(string assemblyLocation = null)
{
// Try to divine our our own install location via reading tea leaves
//
// * We're Update.exe, running in the app's install folder
// * We're Update.exe, running on initial install from SquirrelTemp
// * We're a C# EXE with Squirrel linked in
var assembly = Assembly.GetEntryAssembly();
if (assemblyLocation == null && assembly == null) {
// dunno lol
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
}
assemblyLocation = assemblyLocation ?? assembly.Location;
if (Path.GetFileName(assemblyLocation).Equals("update.exe", StringComparison.OrdinalIgnoreCase)) {
// NB: Both the "SquirrelTemp" case and the "App's folder" case
// mean that the root app dir is one up
var oneFolderUpFromAppFolder = Path.Combine(Path.GetDirectoryName(assemblyLocation), "..");
return Path.GetFullPath(oneFolderUpFromAppFolder);
}
var twoFoldersUpFromAppFolder = Path.Combine(Path.GetDirectoryName(assemblyLocation), "..\\..");
return Path.GetFullPath(twoFoldersUpFromAppFolder);
}
~UpdateManager()
{
if (updateLock != null && !exiting) {
throw new Exception("You must dispose UpdateManager!");
}
}
Task<IDisposable> acquireUpdateLock()
{
if (updateLock != null) return Task.FromResult(updateLock);
return Task.Run(() => {
var key = Utility.CalculateStreamSHA1(new MemoryStream(Encoding.UTF8.GetBytes(rootAppDirectory)));
IDisposable theLock;
try {
theLock = ModeDetector.InUnitTestRunner() ?
Disposable.Create(() => {}) : new SingleGlobalInstance(key, TimeSpan.FromMilliseconds(2000));
} catch (TimeoutException) {
throw new TimeoutException("Couldn't acquire update lock, another instance may be running updates");
}
var ret = Disposable.Create(() => {
theLock.Dispose();
updateLock = null;
});
updateLock = ret;
return ret;
});
}
/// <summary>
/// Calculates the total percentage of a specific step that should report within a specific range.
/// <para />
/// If a step needs to report between 50 -> 75 %, this method should be used as CalculateProgress(percentage, 50, 75).
/// </summary>
/// <param name="percentageOfCurrentStep">The percentage of the current step, a value between 0 and 100.</param>
/// <param name="stepStartPercentage">The start percentage of the range the current step represents.</param>
/// <param name="stepEndPercentage">The end percentage of the range the current step represents.</param>
/// <returns>The calculated percentage that can be reported about the total progress.</returns>
internal static int CalculateProgress(int percentageOfCurrentStep, int stepStartPercentage, int stepEndPercentage)
{
// Ensure we are between 0 and 100
percentageOfCurrentStep = Math.Max(Math.Min(percentageOfCurrentStep, 100), 0);
var range = stepEndPercentage - stepStartPercentage;
var singleValue = range / 100d;
var totalPercentage = (singleValue * percentageOfCurrentStep) + stepStartPercentage;
return (int)totalPercentage;
}
static string getApplicationName()
{
var fi = new FileInfo(getUpdateExe());
return fi.Directory.Name;
}
static string getUpdateExe()
{
var assembly = Assembly.GetEntryAssembly();
// Are we update.exe?
if (assembly != null &&
Path.GetFileName(assembly.Location).Equals("update.exe", StringComparison.OrdinalIgnoreCase) &&
assembly.Location.IndexOf("app-", StringComparison.OrdinalIgnoreCase) == -1 &&
assembly.Location.IndexOf("SquirrelTemp", StringComparison.OrdinalIgnoreCase) == -1) {
return Path.GetFullPath(assembly.Location);
}
assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
var updateDotExe = Path.Combine(Path.GetDirectoryName(assembly.Location), "..\\Update.exe");
var target = new FileInfo(updateDotExe);
if (!target.Exists) throw new Exception("Update.exe not found, not a Squirrel-installed app?");
return target.FullName;
}
}
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.