View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001728 | OpenMPT | Installer and Update | public | 2023-09-11 17:22 | 2025-06-14 06:38 |
Reporter | Saga Musix | Assigned To | manx | ||
Priority | normal | Severity | minor | Reproducibility | have not tried |
Status | resolved | Resolution | fixed | ||
Target Version | OpenMPT 1.33 / libopenmpt 0.9 (goals) | Fixed in Version | OpenMPT 1.33 / libopenmpt 0.9 (goals) | ||
Summary | 0001728: Rewrite portable updater in another language | ||||
Description | According to https://winaero.com/vbscript-has-become-optional-component-in-windows-11/ it appears that the VBScript interpreter has become an optional component in Windows 11. From my understanding, it will still be installed by default, but it is possible for individual users to uninstall the interpreter, which would break the OpenMPT update mechanism for portable versions. If it's true that it will still be installed by default on all Windows 11 installations, there is no urgency in this, but I think we should explore potential alternatives.
| ||||
Tags | No tags attached. | ||||
Has the bug occurred in previous versions? | |||||
Tested code revision (in case you know it) | |||||
Only mildly tested in isolation for now. The patch uses PowerShell instead of VBScript from Windows 11 onwards. TODO:
update-portable-powershell-v1.patch (7,686 bytes)
Index: mptrack/UpdateCheck.cpp =================================================================== --- mptrack/UpdateCheck.cpp (revision 23480) +++ mptrack/UpdateCheck.cpp (working copy) @@ -937,7 +937,7 @@ -static const char updateScript[] = R"vbs( +static const char updateScript_vbs[] = R"vbs( Wscript.Echo Wscript.Echo "OpenMPT portable Update" @@ -1016,7 +1016,59 @@ )vbs"; +static const char updateScript_ps1[] = R"ps1( +param( + [String]$zip="", + [String]$subfolder="", + [String]$dst="", + [String]$restartbinary="") + +Write-Output "" +Write-Output "OpenMPT portable Update" +Write-Output "=======================" + +Write-Output "[ 0%] Waiting for OpenMPT to close..." +Start-Sleep -Seconds 2 + +Write-Output "[ 10%] Changing to temporary directory..." +Set-Location -Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) + +Write-Output "[ 20%] Decompressing update..." +Expand-Archive -Path $zip -DestinationPath (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -Force + +Write-Output "[ 40%] Installing update..." +if (($subfolder -eq "") -or ($subfolder -eq ".")) { + Copy-Item -Path (Join-Path -Path (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -ChildPath "*") -Destination $dst -Recurse -Force +} else { + Copy-Item -Path (Join-Path -Path (Join-Path -Path (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -ChildPath $subfolder) -ChildPath "*") -Destination $dst -Recurse -Force +} + +Write-Output "[ 60%] Deleting temporary directory..." +Remove-Item -Path (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -Recurse -Force + +Write-Output "[ 80%] Restarting OpenMPT..." +Start-Process -FilePath (Join-Path -Path (Resolve-Path -Path $dst) -ChildPath $restartbinary) -WorkingDirectory $dst + +Write-Output "[100%] Update successful!" +Write-Output "" +Start-Sleep -Seconds 1 + +Write-Output "Closing update window in 5 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 4 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 3 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 2 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 1 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window..." + +)ps1"; + + class CDoUpdate: public CProgressDialog { private: @@ -1306,35 +1358,75 @@ } } else if(download.type == U_("archive") && downloadinfo.autoupdate_archive) { - try + if(mpt::osinfo::windows::Version::Current().IsAtLeast(mpt::osinfo::windows::Version::Win10, 22000)) { - mpt::IO::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.vbs"), std::ios::binary); - file.stream().imbue(std::locale::classic()); - file.stream().exceptions(std::ios::failbit | std::ios::badbit); - mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript))); - } catch(...) + try + { + mpt::IO::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.ps1"), std::ios::binary); + file.stream().imbue(std::locale::classic()); + file.stream().exceptions(std::ios::failbit | std::ios::badbit); + mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript_ps1))); + } catch(...) + { + throw Error(U_("Error creating update script.")); + } + std::vector<mpt::ustring> arguments; + arguments.push_back(U_("-ExecutionPolicy")); + arguments.push_back(U_("Unrestricted")); + arguments.push_back(U_("\"") + (dirTempOpenMPTUpdates + P_("update.ps1")).ToUnicode() + U_("\"")); + arguments.push_back(U_("-zip")); + arguments.push_back(U_("\"") + updateFilename.ToUnicode() + U_("\"")); + arguments.push_back(U_("-subfolder")); + arguments.push_back(U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\"")); + arguments.push_back(U_("-dst")); + arguments.push_back(U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\"")); + arguments.push_back(U_("-restartbinary")); + arguments.push_back(U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\"")); + if(theApp.IsSourceTreeMode()) + { + throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::join_format(arguments, U_(" ")))); + } + if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL, + P_("powershell").AsNative().c_str(), + mpt::ToWin(mpt::join_format(arguments, U_(" "))).c_str(), + dirTempOpenMPTUpdates.AsNative().c_str(), + SW_SHOWDEFAULT)) < 32) + { + throw Error(U_("Error launching update.")); + } + wantClose = true; + } else { - throw Error(U_("Error creating update script.")); + try + { + mpt::IO::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.vbs"), std::ios::binary); + file.stream().imbue(std::locale::classic()); + file.stream().exceptions(std::ios::failbit | std::ios::badbit); + mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript_vbs))); + } catch(...) + { + throw Error(U_("Error creating update script.")); + } + std::vector<mpt::ustring> arguments; + arguments.push_back(U_("\"") + (dirTempOpenMPTUpdates + P_("update.vbs")).ToUnicode() + U_("\"")); + arguments.push_back(U_("\"") + updateFilename.ToUnicode() + U_("\"")); + arguments.push_back(U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\"")); + arguments.push_back(U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\"")); + arguments.push_back(U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\"")); + if(theApp.IsSourceTreeMode()) + { + throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::join_format(arguments, U_(" ")))); + } + if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL, + P_("cscript.exe").AsNative().c_str(), + mpt::ToWin(mpt::join_format(arguments, U_(" "))).c_str(), + dirTempOpenMPTUpdates.AsNative().c_str(), + SW_SHOWDEFAULT)) < 32) + { + throw Error(U_("Error launching update.")); + } + wantClose = true; } - std::vector<mpt::ustring> arguments; - arguments.push_back(U_("\"") + (dirTempOpenMPTUpdates + P_("update.vbs")).ToUnicode() + U_("\"")); - arguments.push_back(U_("\"") + updateFilename.ToUnicode() + U_("\"")); - arguments.push_back(U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\"")); - arguments.push_back(U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\"")); - arguments.push_back(U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\"")); - if(theApp.IsSourceTreeMode()) - { - throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::join_format(arguments, U_(" ")))); - } - if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL, - P_("cscript.exe").AsNative().c_str(), - mpt::ToWin(mpt::join_format(arguments, U_(" "))).c_str(), - dirTempOpenMPTUpdates.AsNative().c_str(), - SW_SHOWDEFAULT)) < 32) - { - throw Error(U_("Error launching update.")); - } - wantClose = true; } else { CTrackApp::OpenDirectory(dirTempOpenMPTUpdates); |
|
The version of PowerShell shipped by any Windows 11 is at least 5.1 (see https://learn.microsoft.com/en-us/powershell/scripting/install/powershell-support-lifecycle?view=powershell-7.5#windows-powershell-release-history), which is also the latest version that is shipped with Windows. This concludes that no further compatibility testing needs to be done on Windows 11. |
|
update-portable-powershell-v2.patch (8,827 bytes)
Index: mptrack/TrackerSettings.cpp =================================================================== --- mptrack/TrackerSettings.cpp (revision 23480) +++ mptrack/TrackerSettings.cpp (working copy) @@ -358,6 +358,7 @@ , UpdateIgnoreVersion(conf, UL_("Update"), UL_("IgnoreVersion"), _T("")) , UpdateSkipSignatureVerificationUNSECURE(conf, UL_("Update"), UL_("SkipSignatureVerification"), false) , UpdateSigningKeysRootAnchors(conf, UL_("Update"), UL_("SigningKeysRootAnchors"), CUpdateCheck::GetDefaultUpdateSigningKeysRootAnchors()) + , UpdatePortableBackend(conf, UL_("Update"), UL_("PortableBackend"), 0) #endif // MPT_ENABLE_UPDATE // Wine suppport , WineSupportEnabled(conf, UL_("WineSupport"), UL_("Enabled"), false) Index: mptrack/TrackerSettings.h =================================================================== --- mptrack/TrackerSettings.h (revision 23480) +++ mptrack/TrackerSettings.h (working copy) @@ -1002,6 +1002,7 @@ Setting<CString> UpdateIgnoreVersion; Setting<bool> UpdateSkipSignatureVerificationUNSECURE; Setting<std::vector<mpt::ustring>> UpdateSigningKeysRootAnchors; + Setting<int32> UpdatePortableBackend; #endif // MPT_ENABLE_UPDATE Index: mptrack/UpdateCheck.cpp =================================================================== --- mptrack/UpdateCheck.cpp (revision 23480) +++ mptrack/UpdateCheck.cpp (working copy) @@ -937,7 +937,7 @@ -static const char updateScript[] = R"vbs( +static const char updateScript_vbs[] = R"vbs( Wscript.Echo Wscript.Echo "OpenMPT portable Update" @@ -1016,7 +1016,59 @@ )vbs"; +static const char updateScript_ps1[] = R"ps1( +param( + [String]$zip="", + [String]$subfolder="", + [String]$dst="", + [String]$restartbinary="") + +Write-Output "" +Write-Output "OpenMPT portable Update" +Write-Output "=======================" + +Write-Output "[ 0%] Waiting for OpenMPT to close..." +Start-Sleep -Seconds 2 + +Write-Output "[ 10%] Changing to temporary directory..." +Set-Location -Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) + +Write-Output "[ 20%] Decompressing update..." +Expand-Archive -Path $zip -DestinationPath (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -Force + +Write-Output "[ 40%] Installing update..." +if (($subfolder -eq "") -or ($subfolder -eq ".")) { + Copy-Item -Path (Join-Path -Path (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -ChildPath "*") -Destination $dst -Recurse -Force +} else { + Copy-Item -Path (Join-Path -Path (Join-Path -Path (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -ChildPath $subfolder) -ChildPath "*") -Destination $dst -Recurse -Force +} + +Write-Output "[ 60%] Deleting temporary directory..." +Remove-Item -Path (Join-Path -Path (Resolve-Path -Path ".") -ChildPath "tmp") -Recurse -Force + +Write-Output "[ 80%] Restarting OpenMPT..." +Start-Process -FilePath (Join-Path -Path (Resolve-Path -Path $dst) -ChildPath $restartbinary) -WorkingDirectory $dst + +Write-Output "[100%] Update successful!" +Write-Output "" +Start-Sleep -Seconds 1 + +Write-Output "Closing update window in 5 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 4 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 3 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 2 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window in 1 seconds..." +Start-Sleep -Seconds 1 +Write-Output "Closing update window..." + +)ps1"; + + class CDoUpdate: public CProgressDialog { private: @@ -1306,33 +1358,82 @@ } } else if(download.type == U_("archive") && downloadinfo.autoupdate_archive) { - try + bool usePowerShell = mpt::osinfo::windows::Version::Current().IsAtLeast(mpt::osinfo::windows::Version::Win10, 22000); + switch(TrackerSettings::Instance().UpdatePortableBackend) { - mpt::IO::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.vbs"), std::ios::binary); - file.stream().imbue(std::locale::classic()); - file.stream().exceptions(std::ios::failbit | std::ios::badbit); - mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript))); - } catch(...) - { - throw Error(U_("Error creating update script.")); + case 1: + usePowerShell = false; + break; + case 2: + usePowerShell = true; + break; + default: + // nothing + break; } - std::vector<mpt::ustring> arguments; - arguments.push_back(U_("\"") + (dirTempOpenMPTUpdates + P_("update.vbs")).ToUnicode() + U_("\"")); - arguments.push_back(U_("\"") + updateFilename.ToUnicode() + U_("\"")); - arguments.push_back(U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\"")); - arguments.push_back(U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\"")); - arguments.push_back(U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\"")); - if(theApp.IsSourceTreeMode()) + if(usePowerShell) { - throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::join_format(arguments, U_(" ")))); - } - if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL, - P_("cscript.exe").AsNative().c_str(), - mpt::ToWin(mpt::join_format(arguments, U_(" "))).c_str(), - dirTempOpenMPTUpdates.AsNative().c_str(), - SW_SHOWDEFAULT)) < 32) + try + { + mpt::IO::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.ps1"), std::ios::binary); + file.stream().imbue(std::locale::classic()); + file.stream().exceptions(std::ios::failbit | std::ios::badbit); + mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript_ps1))); + } catch(...) + { + throw Error(U_("Error creating update script.")); + } + std::vector<mpt::ustring> arguments = { + U_("-NoLogo"), + U_("-ExecutionPolicy"), U_("Unrestricted"), + U_("\"") + (dirTempOpenMPTUpdates + P_("update.ps1")).ToUnicode() + U_("\""), + U_("-zip"), U_("\"") + updateFilename.ToUnicode() + U_("\""), + U_("-subfolder"), U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\""), + U_("-dst"), U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\""), + U_("-restartbinary"), U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\"") + }; + if(theApp.IsSourceTreeMode()) + { + throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::join_format(arguments, U_(" ")))); + } + if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL, + P_("PowerShell.exe").AsNative().c_str(), + mpt::ToWin(mpt::join_format(arguments, U_(" "))).c_str(), + dirTempOpenMPTUpdates.AsNative().c_str(), + SW_SHOWDEFAULT)) < 32) + { + throw Error(U_("Error launching update.")); + } + } else { - throw Error(U_("Error launching update.")); + try + { + mpt::IO::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.vbs"), std::ios::binary); + file.stream().imbue(std::locale::classic()); + file.stream().exceptions(std::ios::failbit | std::ios::badbit); + mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript_vbs))); + } catch(...) + { + throw Error(U_("Error creating update script.")); + } + std::vector<mpt::ustring> arguments; + arguments.push_back(U_("\"") + (dirTempOpenMPTUpdates + P_("update.vbs")).ToUnicode() + U_("\"")); + arguments.push_back(U_("\"") + updateFilename.ToUnicode() + U_("\"")); + arguments.push_back(U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\"")); + arguments.push_back(U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\"")); + arguments.push_back(U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\"")); + if(theApp.IsSourceTreeMode()) + { + throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::join_format(arguments, U_(" ")))); + } + if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL, + P_("cscript.exe").AsNative().c_str(), + mpt::ToWin(mpt::join_format(arguments, U_(" "))).c_str(), + dirTempOpenMPTUpdates.AsNative().c_str(), + SW_SHOWDEFAULT)) < 32) + { + throw Error(U_("Error launching update.")); + } } wantClose = true; } else |
|
r23483 / 1.33.00.06 |
|
Date Modified | Username | Field | Change |
---|---|---|---|
2023-09-11 17:22 | Saga Musix | New Issue | |
2023-09-11 22:25 | Saga Musix | Description Updated | |
2024-10-10 08:15 | manx | Category | General => Installer and Update |
2025-06-13 15:56 | manx | Assigned To | => manx |
2025-06-13 15:56 | manx | Status | new => assigned |
2025-06-13 15:58 | manx | Note Added: 0006393 | |
2025-06-13 15:58 | manx | File Added: update-portable-powershell-v1.patch | |
2025-06-13 15:59 | manx | Target Version | => OpenMPT 1.33 / libopenmpt 0.9 (goals) |
2025-06-13 17:24 | manx | Note Added: 0006394 | |
2025-06-13 17:25 | manx | Note Edited: 0006393 | |
2025-06-13 17:48 | manx | Note Added: 0006395 | |
2025-06-13 17:48 | manx | File Added: update-portable-powershell-v2.patch | |
2025-06-14 06:36 | manx | Note Edited: 0006393 | |
2025-06-14 06:38 | manx | Status | assigned => resolved |
2025-06-14 06:38 | manx | Resolution | open => fixed |
2025-06-14 06:38 | manx | Fixed in Version | => OpenMPT 1.33 / libopenmpt 0.9 (goals) |
2025-06-14 06:38 | manx | Note Added: 0006396 |