Skip to content

Commit 345a3d6

Browse files
committed
#5346 Uninstall older non-velopack viewer
if of the same channel
1 parent 5f1dbb6 commit 345a3d6

5 files changed

Lines changed: 174 additions & 0 deletions

File tree

indra/cmake/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ set(cmake_SOURCE_FILES
6262
UI.cmake
6363
UnixInstall.cmake
6464
Variables.cmake
65+
Velopack.cmake
6566
VHACD.cmake
6667
ViewerMiscLibs.cmake
6768
VisualLeakDetector.cmake

indra/newview/llstartup.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
#include "llappviewer.h"
3030
#include "llstartup.h"
3131

32+
#if LL_VELOPACK && LL_WINDOWS
33+
#include "llvelopack.h"
34+
#include <shellapi.h>
35+
#endif
36+
3237
#if LL_WINDOWS
3338
# include <process.h> // _spawnl()
3439
#else
@@ -266,6 +271,7 @@ std::unique_ptr<LLViewerStats::PhaseMap> LLStartUp::sPhases(new LLViewerStats::P
266271

267272
void login_show();
268273
void login_callback(S32 option, void* userdata);
274+
void uninstall_nsis_if_required();
269275
void show_release_notes_if_required();
270276
void show_first_run_dialog();
271277
bool first_run_dialog_callback(const LLSD& notification, const LLSD& response);
@@ -855,6 +861,7 @@ bool idle_startup()
855861
initialize_spellcheck_menu();
856862
init_menus();
857863
}
864+
uninstall_nsis_if_required();
858865
show_release_notes_if_required();
859866

860867
if (show_connect_box)
@@ -2626,6 +2633,60 @@ void validate_release_notes_coro(const std::string url)
26262633
}
26272634
}
26282635

2636+
/**
2637+
* Check if this is a fresh velopack install and
2638+
* if uninstallation of old viewer is needed.
2639+
*/
2640+
void uninstall_nsis_if_required()
2641+
{
2642+
#if LL_VELOPACK && LL_WINDOWS
2643+
if (!LLVelopack::sAfterInstall)
2644+
{
2645+
// Only fresh installs
2646+
return;
2647+
}
2648+
2649+
wchar_t buffer[MAX_PATH];
2650+
if (!get_nsis_uninstaller_path(buffer, MAX_PATH))
2651+
{
2652+
return;
2653+
}
2654+
2655+
LLNotificationsUtil::add("PromptRemoveNsisInstallation", LLSD(), LLSD(),
2656+
[buffer](const LLSD& notification, const LLSD& response)
2657+
{
2658+
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
2659+
if (option == 1) // cancel
2660+
{
2661+
return;
2662+
}
2663+
2664+
// Launch uninstaller using explorer.exe
2665+
SHELLEXECUTEINFOW sei = { 0 };
2666+
sei.cbSize = sizeof(sei);
2667+
sei.fMask = SEE_MASK_DEFAULT;
2668+
sei.hwnd = NULL;
2669+
//sei.lpVerb = L"runas"; // Request elevation
2670+
sei.lpFile = L"explorer.exe";
2671+
sei.lpParameters = nullptr;
2672+
2673+
// Compose command line: "<uninstaller_path>" /S /clearreg
2674+
std::wstring params = L"\"";
2675+
params += buffer;
2676+
params += L"\"";
2677+
params += L" /S /clearreg"; // silent uninstall and clear registry entries
2678+
sei.lpParameters = params.c_str();
2679+
2680+
sei.nShow = SW_HIDE;
2681+
2682+
if (!ShellExecuteExW(&sei))
2683+
{
2684+
LL_WARNS("AppInit") << "Failed to launch NSIS uninstaller, error code: " << GetLastError() << LL_ENDL;
2685+
}
2686+
});
2687+
#endif
2688+
}
2689+
26292690
/**
26302691
* Check if user is running a new version of the viewer.
26312692
* Display the Release Notes if it's not overriden by the "UpdaterShowReleaseNotes" setting.

indra/newview/llvelopack.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "llviewerprecompiledheaders.h"
3030
#include "llvelopack.h"
31+
#include "llstring.h"
3132

3233
#include "Velopack.h"
3334

@@ -37,6 +38,7 @@
3738
#include <shobjidl.h>
3839
#include <shlwapi.h>
3940
#include <objbase.h>
41+
#include <filesystem>
4042

4143
#pragma comment(lib, "shlwapi.lib")
4244
#pragma comment(lib, "ole32.lib")
@@ -175,6 +177,81 @@ static void register_protocol_handler(const std::wstring& protocol,
175177
}
176178
}
177179

180+
bool get_nsis_uninstaller_path(wchar_t* path_buffer, DWORD bufSize)
181+
{
182+
// Test for presense of NSIS viewer registration, then
183+
// attempt to read uninstall info
184+
std::wstring app_name_oneword = get_app_name_oneword();
185+
std::wstring uninstall_key_path = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + app_name_oneword;
186+
HKEY hkey;
187+
LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, uninstall_key_path.c_str(), 0, KEY_READ, &hkey);
188+
if (result != ERROR_SUCCESS)
189+
{
190+
return false;
191+
}
192+
193+
// Read DisplayVersion
194+
wchar_t version_buf[64] = { 0 };
195+
DWORD version_buf_size = sizeof(version_buf);
196+
DWORD type = 0;
197+
LONG ver_rv = RegGetValueW(hkey, nullptr, L"DisplayVersion", RRF_RT_REG_SZ, &type, version_buf, &version_buf_size);
198+
// Compare versions if both are available
199+
if (ver_rv == ERROR_SUCCESS)
200+
{
201+
// Get current version as UTF-8 string, convert to wstring
202+
std::string current_version_str = velopack_get_current_version();
203+
std::wstring current_version_w;
204+
if (!current_version_str.empty())
205+
{
206+
int wlen = MultiByteToWideChar(CP_UTF8, 0, current_version_str.c_str(), -1, nullptr, 0);
207+
if (wlen > 0)
208+
{
209+
current_version_w.resize(wlen - 1);
210+
MultiByteToWideChar(CP_UTF8, 0, current_version_str.c_str(), -1, &current_version_w[0], wlen);
211+
}
212+
}
213+
// If NSIS version is newer, skip removal
214+
// todo: for testing purposes this ignores missing version,
215+
// should skip if current version is uknown
216+
if (!current_version_w.empty() && wcscmp(version_buf, current_version_w.c_str()) > 0)
217+
{
218+
RegCloseKey(hkey);
219+
return false;
220+
}
221+
}
222+
223+
LONG rv = RegGetValueW(hkey, nullptr, L"UninstallString", RRF_RT_REG_SZ, &type, path_buffer, &bufSize);
224+
RegCloseKey(hkey);
225+
if (rv != ERROR_SUCCESS)
226+
{
227+
return false;
228+
}
229+
size_t len = wcslen(path_buffer);
230+
if (len > 0)
231+
{
232+
if (path_buffer[0] == L'\"')
233+
{
234+
// Shift all characters left by one, including the null terminator
235+
memmove(path_buffer, path_buffer + 1, (len) * sizeof(wchar_t));
236+
}
237+
238+
wchar_t* pos = wcsstr(path_buffer, L"uninst.exe");
239+
if (pos)
240+
{
241+
pos[wcslen(L"uninst.exe")] = L'\0';
242+
}
243+
}
244+
std::error_code ec;
245+
std::filesystem::path path(path_buffer);
246+
if (!std::filesystem::exists(path, ec))
247+
{
248+
// If ec, assume that it's there?
249+
return false;
250+
}
251+
252+
return true;
253+
}
254+
178255
static void unregister_protocol_handler(const std::wstring& protocol)
179256
{
180257
std::wstring key_path = L"SOFTWARE\\Classes\\" + protocol;
@@ -206,6 +283,18 @@ static void register_uninstall_info(const std::wstring& install_dir,
206283
RegSetValueExW(hkey, L"DisplayIcon", 0, REG_SZ,
207284
(BYTE*)exe_path.c_str(), (DWORD)((exe_path.size() + 1) * sizeof(wchar_t)));
208285

286+
std::wstring link_url = L"https://support.secondlife.com/contact-support/";
287+
RegSetValueExW(hkey, L"HelpLink", 0, REG_SZ,
288+
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
289+
290+
link_url = L"https://secondlife.com/whatis/";
291+
RegSetValueExW(hkey, L"URLInfoAbout", 0, REG_SZ,
292+
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
293+
294+
link_url = L"http://secondlife.com/support/downloads/";
295+
RegSetValueExW(hkey, L"URLUpdateInfo", 0, REG_SZ,
296+
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
297+
209298
DWORD no_modify = 1;
210299
RegSetValueExW(hkey, L"NoModify", 0, REG_DWORD, (BYTE*)&no_modify, sizeof(DWORD));
211300
RegSetValueExW(hkey, L"NoRepair", 0, REG_DWORD, (BYTE*)&no_modify, sizeof(DWORD));
@@ -251,6 +340,8 @@ static void remove_shortcuts(const std::wstring& app_name)
251340

252341
static void on_after_install(void* user_data, const char* app_version)
253342
{
343+
LLVelopack::sAfterInstall = true;
344+
254345
std::wstring install_dir = get_install_dir();
255346
std::wstring app_name = get_app_name();
256347
std::wstring exe_path = install_dir + L"\\" + VIEWER_EXE_NAME;

indra/newview/llvelopack.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
#include <string>
3333
#include <functional>
3434

35+
namespace LLVelopack
36+
{
37+
static bool sAfterInstall = false;
38+
}
39+
3540
bool velopack_initialize();
3641
void velopack_check_for_updates();
3742
std::string velopack_get_current_version();
@@ -40,6 +45,10 @@ void velopack_apply_pending_update(bool restart = true);
4045
void velopack_set_update_url(const std::string& url);
4146
void velopack_set_progress_callback(std::function<void(int)> callback);
4247

48+
#if LL_WINDOWS
49+
bool get_nsis_uninstaller_path(wchar_t* path_buffer, DWORD bufSize);
50+
#endif
51+
4352
#endif // LL_VELOPACK
4453
#endif
4554
// EOF

indra/newview/skins/default/xui/en/notifications.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,18 @@ No tutorial is currently available.
200200
yestext="OK"/>
201201
</notification>
202202

203+
<notification
204+
icon="alertmodal.tga"
205+
name="PromptRemoveNsisInstallation"
206+
type="alertmodal">
207+
[APP_NAME] found installation from an older version. Do you want to uninstall previous version? Uninstaller might request access to files on disk.
208+
<tag>confirm</tag>
209+
<usetemplate
210+
name="okcancelbuttons"
211+
notext="Cancel"
212+
yestext="Uninstall"/>
213+
</notification>
214+
203215
<notification
204216
icon="alertmodal.tga"
205217
name="LoginFailedNoNetwork"

0 commit comments

Comments
 (0)