Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: src/engine/Updater.cpp

Issue 10920006: Expect MSI installers (Closed)
Left Patch Set: Created June 10, 2013, 3:27 p.m.
Right Patch Set: Separate function to run updater, using msiexec now Created June 13, 2013, 2:32 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « AdblockPlusEngine.vcxproj ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 #include <functional> 1 #include <functional>
2 #include <memory> 2 #include <memory>
3 #include <sstream> 3 #include <sstream>
4 4
5 #include <Windows.h> 5 #include <Windows.h>
6 #include <Msi.h>
7 6
8 #include <AdblockPlus/FileSystem.h> 7 #include <AdblockPlus/FileSystem.h>
9 #include <AdblockPlus/WebRequest.h> 8 #include <AdblockPlus/WebRequest.h>
10 9
10 #include "../shared/AutoHandle.h"
11 #include "../shared/Dictionary.h" 11 #include "../shared/Dictionary.h"
12 #include "../shared/Utils.h" 12 #include "../shared/Utils.h"
13 #include "Debug.h" 13 #include "Debug.h"
14 #include "Resource.h" 14 #include "Resource.h"
15 #include "Updater.h" 15 #include "Updater.h"
16 16
17 namespace 17 namespace
18 { 18 {
19 typedef std::function<void()> ThreadCallbackType; 19 typedef std::function<void()> ThreadCallbackType;
20 typedef std::function<void(HWND)> DialogCallbackType; 20 typedef std::function<void(HWND)> DialogCallbackType;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 } 78 }
79 return FALSE; 79 return FALSE;
80 } 80 }
81 81
82 DWORD RunThread(LPVOID param) 82 DWORD RunThread(LPVOID param)
83 { 83 {
84 std::auto_ptr<ThreadCallbackType> callback(reinterpret_cast<ThreadCallbackTy pe*>(param)); 84 std::auto_ptr<ThreadCallbackType> callback(reinterpret_cast<ThreadCallbackTy pe*>(param));
85 (*callback)(); 85 (*callback)();
86 return 0; 86 return 0;
87 } 87 }
88
89 std::wstring EscapeCommandLineArg(const std::wstring& arg)
90 {
91 // This does the inverse of CommandLineToArgvW(). See
92 // http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx for
93 // a description of the rules - the backslash rules are very non-obvious.
94 std::wstring result = arg;
95 size_t pos = arg.find(L'"');
96 while (pos != std::wstring::npos)
97 {
98 // Protect the quotation mark
99 result.insert(pos, 1, L'\\');
100 pos++;
101
102 // Protect any of the preceding backslashes
103 for (int offset = -2; pos + offset >= 0 && result[pos + offset] == L'\\'; offset -= 2)
104 {
105 result.insert(pos + offset, 1, L'\\');
106 pos++;
107 }
108
109 // Find next quotation mark
110 pos = arg.find(L'"', pos);
111 }
112 return L'"' + result + L'"';
113 }
114
115 BOOL InstallUpdate(const std::wstring& path)
116 {
117 WCHAR sysDir[MAX_PATH];
118 UINT sysDirLen = GetSystemDirectoryW(sysDir, sizeof(sysDir) / sizeof(sysDir[ 0]));
119 if (sysDirLen == 0)
120 return false;
121
122 std::wstring msiexec = std::wstring(sysDir, sysDirLen) + L"\\msiexec.exe";
123
124 std::wstring params = L"/i " + EscapeCommandLineArg(path)
125 + L" ACTION=INSTALL INSTALLUILEVEL=2 REINSTALL=ALL"
126 L" REINSTALLMODE=vomus MSIENFORCEUPGRADECOMPONENTRULES=1";
127
128 HINSTANCE instance = ShellExecuteW(NULL, L"runas", msiexec.c_str(), params.c _str(), NULL, SW_HIDE);
129 if (reinterpret_cast<int>(instance) <= 32)
130 return false;
131
132 // As far as we are concerned everything is fine - MSI service will handle
133 // further errors.
134 return true;
135 }
88 } 136 }
89 137
90 Updater::Updater(AdblockPlus::JsEnginePtr jsEngine, const std::string& url) 138 Updater::Updater(AdblockPlus::JsEnginePtr jsEngine, const std::string& url)
91 : jsEngine(jsEngine), url(url), tempFile(GetAppDataPath() + L"\\update.msi") 139 : jsEngine(jsEngine), url(url), tempFile(GetAppDataPath() + L"\\update.msi")
92 { 140 {
93 } 141 }
94 142
95 void Updater::Update() 143 void Updater::Update()
96 { 144 {
97 Debug("Update available: " + url); 145 Debug("Update available: " + url);
98 146
99 if (DialogBox(NULL, MAKEINTRESOURCE(IDD_UPDATEDIALOG), GetDesktopWindow(), 147 if (DialogBox(NULL, MAKEINTRESOURCE(IDD_UPDATEDIALOG), GetDesktopWindow(),
100 reinterpret_cast<DLGPROC>(&UpdateDlgProc)) == IDOK) 148 reinterpret_cast<DLGPROC>(&UpdateDlgProc)) == IDOK)
101 { 149 {
102 Debug("User accepted update"); 150 Debug("User accepted update");
103 151
104 { 152 DialogCallbackType* callback = new DialogCallbackType(std::bind(&Updater::St artDownload,
Felix Dahlke 2013/06/11 10:07:52 I presume the two blocks are to avoid conflicts be
Wladimir Palant 2013/06/11 14:42:10 I would rather have distinct scopes for these two
105 DialogCallbackType* callback = new DialogCallbackType(std::bind(&Updater:: StartDownload, 153 this, std::placeholders::_1));
106 this, std::placeholders::_1)); 154 int result = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_DOWNLOADDIALOG), GetDe sktopWindow(),
107 int result = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_DOWNLOADDIALOG), Get DesktopWindow(), 155 reinterpret_cast<DLGPROC>(&DownloadDlgProc),
108 reinterpret_cast<DLGPROC>(&DownloadDlgProc), 156 reinterpret_cast<LPARAM>(callback));
109 reinterpret_cast<LPARAM>(callback)); 157 if (result == DOWNLOAD_FAILED)
110 if (result == DOWNLOAD_FAILED) 158 {
111 { 159 Dictionary* dict = Dictionary::GetInstance();
112 Dictionary* dict = Dictionary::GetInstance(); 160 MessageBoxW(NULL,
113 MessageBoxW(NULL, 161 dict->Lookup("updater", "download-error-neterror").c_str(),
114 dict->Lookup("updater", "download-error-neterror").c_str(), 162 dict->Lookup("updater", "download-error-title").c_str(),
115 dict->Lookup("updater", "download-error-title").c_str(), 163 0);
116 0); 164 }
117 } 165 if (result != IDOK)
118 if (result != IDOK) 166 return;
119 return; 167
120 } 168 if (!InstallUpdate(tempFile))
121 169 {
122 { 170 DebugLastError("Running updater failed");
123 UINT result = ::MsiInstallProductW(tempFile.c_str(), L"ACTION=INSTALL INST ALLUILEVEL=2"); 171
Oleksandr 2013/06/10 16:47:56 Wouldn't it be better just to launch like "msiexec
Wladimir Palant 2013/06/11 04:29:56 MsiInstallProduct is IMHO a much cleaner solution
Felix Dahlke 2013/06/11 10:07:52 As long as "update.msi" is hardcoded above we'd ne
Oleksandr 2013/06/11 10:24:49 That's my point. Stuff happens, and maybe for some
Felix Dahlke 2013/06/11 10:28:05 Couldn't we just ship an exe with the msi and exec
Wladimir Palant 2013/06/11 14:42:10 I really don't see why we would need a plan b (one
Felix Dahlke 2013/06/12 13:03:40 I'd vote for two functions then :)
124 if (result != ERROR_SUCCESS) 172 Dictionary* dict = Dictionary::GetInstance();
125 { 173 MessageBoxW(NULL,
126 Dictionary* dict = Dictionary::GetInstance(); 174 dict->Lookup("updater", "download-error-runerror").c_str(),
127 std::wstringstream message; 175 dict->Lookup("updater", "download-error-title").c_str(),
128 message << dict->Lookup("updater", "download-error-runerror"); 176 0);
129 message << std::endl << L"(error " << result << L")";
130 MessageBoxW(NULL,
131 message.str().c_str(),
132 dict->Lookup("updater", "download-error-title").c_str(),
133 0);
134
135 std::stringstream error;
136 error << "Installing update failed (error " << result << ")";
137 Debug(error.str());
138 return;
139 }
140 } 177 }
141 } 178 }
142 } 179 }
143 180
144 void Updater::StartDownload(HWND dialog) 181 void Updater::StartDownload(HWND dialog)
145 { 182 {
146 this->dialog = dialog; 183 this->dialog = dialog;
147 ThreadCallbackType* callback = new ThreadCallbackType(std::bind(&Updater::RunD ownload, this)); 184 ThreadCallbackType* callback = new ThreadCallbackType(std::bind(&Updater::RunD ownload, this));
148 ::CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(&RunThread), 185 ::CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(&RunThread),
149 callback, 0, NULL); 186 callback, 0, NULL);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 } 219 }
183 catch (const std::exception& e) 220 catch (const std::exception& e)
184 { 221 {
185 DebugException(e); 222 DebugException(e);
186 EndDialog(dialog, DOWNLOAD_FAILED); 223 EndDialog(dialog, DOWNLOAD_FAILED);
187 return; 224 return;
188 } 225 }
189 226
190 EndDialog(dialog, IDOK); 227 EndDialog(dialog, IDOK);
191 } 228 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld