diff --git a/build/rebrand_stub/rebrand_stub.c b/build/rebrand_stub/rebrand_stub.c new file mode 100644 index 0000000..fba92fd --- /dev/null +++ b/build/rebrand_stub/rebrand_stub.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +/* + * This just runs 'Element (Riot).exe' with the same args as + * this process was invoked with. This gets around the fact that + * squirrel always tries to run an executable with the same name, + * so fails to restart if the app's name has changed. + */ +void _tmain( int argc, TCHAR *argv[] ) +{ + LPSTR myCmdLine = GetCommandLineA(); + char cmdLine[32767]; + + LPSTR cmdLinePos = cmdLine; + LPSTR toRun = "\"Element (Riot).exe\" "; + strncpy(cmdLinePos, toRun, strlen(toRun)); + cmdLinePos += strlen(toRun); + + if (myCmdLine[0] == '"') ++myCmdLine; + myCmdLine += strlen(argv[0]); + if (myCmdLine[0] == '"') ++myCmdLine; + if (myCmdLine[0] == ' ') ++myCmdLine; + + strncpy(cmdLinePos, myCmdLine, (cmdLine + 32767) - cmdLinePos); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if (!CreateProcess(NULL, + cmdLine, // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi ) // Pointer to PROCESS_INFORMATION structure + ) + { + printf("CreateProcess failed (%d).\n", GetLastError()); + return; + } +} diff --git a/build/rebrand_stub/rebrand_stub.exe b/build/rebrand_stub/rebrand_stub.exe new file mode 100644 index 0000000..eb8a124 Binary files /dev/null and b/build/rebrand_stub/rebrand_stub.exe differ diff --git a/package.json b/package.json index 26409d3..9fe412c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "main": "src/electron-main.js", "version": "1.6.6", "description": "A feature-rich client for Matrix.org", - "author": "Element Communications Ltd.", + "author": "Element", "repository": { "type": "git", "url": "https://github.com/vector-im/riot-desktop" @@ -100,6 +100,7 @@ "directories": { "output": "dist" }, + "afterPack": "scripts/electron_afterPack", "afterSign": "scripts/electron_afterSign", "protocols": [ { diff --git a/scripts/electron_afterPack.js b/scripts/electron_afterPack.js new file mode 100644 index 0000000..9036638 --- /dev/null +++ b/scripts/electron_afterPack.js @@ -0,0 +1,15 @@ +const fsProm = require('fs').promises; +const path = require('path'); + +exports.default = async function(context) { + const { electronPlatformName, appOutDir } = context; + + // Squirrel windows will try to relaunch the app using an executable of the same name as + // before in the new version, so will fail if the executable is now called something else. + // We add a fake Riot.exe that it can run which runs the real one. + // This also gets signed automatically, presumably because electron-build just looks for all + // exe files and signs them all... + if (electronPlatformName === 'win32') { + await fsProm.copyFile('build/rebrand_stub/rebrand_stub.exe', path.join(appOutDir, "Riot.exe")); + } +}; diff --git a/src/squirrelhooks.js b/src/squirrelhooks.js index 728c9cf..1a95f17 100644 --- a/src/squirrelhooks.js +++ b/src/squirrelhooks.js @@ -17,17 +17,20 @@ limitations under the License. const path = require('path'); const spawn = require('child_process').spawn; const {app} = require('electron'); +const fsProm = require('fs').promises; -function runUpdateExe(args, done) { +function runUpdateExe(args) { // Invokes Squirrel's Update.exe which will do things for us like create shortcuts // Note that there's an Update.exe in the app-x.x.x directory and one in the parent // directory: we need to run the one in the parent directory, because it discovers // information about the app by inspecting the directory it's run from. const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); console.log(`Spawning '${updateExe}' with args '${args}'`); - spawn(updateExe, args, { - detached: true, - }).on('close', done); + return new Promise(resolve => { + spawn(updateExe, args, { + detached: true, + }).on('close', resolve); + }); } function checkSquirrelHooks() { @@ -36,10 +39,29 @@ function checkSquirrelHooks() { const cmd = process.argv[1]; const target = path.basename(process.execPath); if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') { - runUpdateExe(['--createShortcut=' + target + ''], app.quit); + Promise.resolve().then(() => { + return runUpdateExe(['--createShortcut=' + target]); + }).then(() => { + // remove the old 'Riot' shortcuts, if they exist (update.exe --removeShortcut doesn't work + // because it always uses the name of the product as the name of the shortcut: the only variable + // is what executable you're linking to) + const appDataDir = process.env.APPDATA; + if (!appDataDir) return; + const startMenuDir = path.join( + appDataDir, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'New Vector Ltd', + ); + return fsProm.rmdir(startMenuDir, { recursive: true }); + }).then(() => { + const oldDesktopShortcut = path.join(app.getPath('desktop'), 'Riot.lnk'); + return fsProm.unlink(oldDesktopShortcut).catch(() => {}); + }).then(() => { + app.quit(); + }); return true; } else if (cmd === '--squirrel-uninstall') { - runUpdateExe(['--removeShortcut=' + target + ''], app.quit); + runUpdateExe(['--removeShortcut=' + target]).then(() => { + app.quit(); + }); return true; } else if (cmd === '--squirrel-obsolete') { app.quit();