updater_win.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "updater.h"
  8. #include "base/platform/win/base_windows_safe_library.h"
  9. bool _debug = false;
  10. wstring updaterName, updaterDir, updateTo, exeName, customWorkingDir, customKeyFile;
  11. bool equal(const wstring &a, const wstring &b) {
  12. return !_wcsicmp(a.c_str(), b.c_str());
  13. }
  14. void updateError(const WCHAR *msg, DWORD errorCode) {
  15. WCHAR errMsg[2048];
  16. LPWSTR errorTextFormatted = nullptr;
  17. auto formatFlags = FORMAT_MESSAGE_FROM_SYSTEM
  18. | FORMAT_MESSAGE_ALLOCATE_BUFFER
  19. | FORMAT_MESSAGE_IGNORE_INSERTS;
  20. FormatMessage(
  21. formatFlags,
  22. NULL,
  23. errorCode,
  24. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  25. (LPWSTR)&errorTextFormatted,
  26. 0,
  27. 0);
  28. auto errorText = errorTextFormatted
  29. ? errorTextFormatted
  30. : L"(Unknown error)";
  31. wsprintf(errMsg, L"%s, error code: %d\nError message: %s", msg, errorCode, errorText);
  32. MessageBox(0, errMsg, L"Update error!", MB_ICONERROR);
  33. LocalFree(errorTextFormatted);
  34. }
  35. HANDLE _logFile = 0;
  36. void openLog() {
  37. if (!_debug || _logFile) return;
  38. wstring logPath = L"DebugLogs";
  39. if (!CreateDirectory(logPath.c_str(), NULL)) {
  40. DWORD errorCode = GetLastError();
  41. if (errorCode && errorCode != ERROR_ALREADY_EXISTS) {
  42. updateError(L"Failed to create log directory", errorCode);
  43. return;
  44. }
  45. }
  46. SYSTEMTIME stLocalTime;
  47. GetLocalTime(&stLocalTime);
  48. static const int maxFileLen = MAX_PATH * 10;
  49. WCHAR logName[maxFileLen];
  50. wsprintf(logName, L"DebugLogs\\%04d%02d%02d_%02d%02d%02d_upd.txt",
  51. stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
  52. stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
  53. _logFile = CreateFile(logName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  54. if (_logFile == INVALID_HANDLE_VALUE) { // :(
  55. updateError(L"Failed to create log file", GetLastError());
  56. _logFile = 0;
  57. return;
  58. }
  59. }
  60. void closeLog() {
  61. if (!_logFile) return;
  62. CloseHandle(_logFile);
  63. _logFile = 0;
  64. }
  65. void writeLog(const wstring &msg) {
  66. if (!_logFile) return;
  67. wstring full = msg + L'\n';
  68. DWORD written = 0;
  69. BOOL result = WriteFile(_logFile, full.c_str(), full.size() * sizeof(wchar_t), &written, 0);
  70. if (!result) {
  71. updateError((L"Failed to write log entry '" + msg + L"'").c_str(), GetLastError());
  72. closeLog();
  73. return;
  74. }
  75. BOOL flushr = FlushFileBuffers(_logFile);
  76. if (!flushr) {
  77. updateError((L"Failed to flush log on entry '" + msg + L"'").c_str(), GetLastError());
  78. closeLog();
  79. return;
  80. }
  81. }
  82. void fullClearPath(const wstring &dir) {
  83. WCHAR path[4096];
  84. memcpy(path, dir.c_str(), (dir.size() + 1) * sizeof(WCHAR));
  85. path[dir.size() + 1] = 0;
  86. writeLog(L"Fully clearing path '" + dir + L"'..");
  87. SHFILEOPSTRUCT file_op = {
  88. NULL,
  89. FO_DELETE,
  90. path,
  91. L"",
  92. FOF_NOCONFIRMATION |
  93. FOF_NOERRORUI |
  94. FOF_SILENT,
  95. false,
  96. 0,
  97. L""
  98. };
  99. int res = SHFileOperation(&file_op);
  100. if (res) writeLog(L"Error: failed to clear path! :(");
  101. }
  102. void delFolder() {
  103. wstring delPathOld = L"tupdates\\ready", delPath = L"tupdates\\temp", delFolder = L"tupdates";
  104. fullClearPath(delPathOld);
  105. fullClearPath(delPath);
  106. RemoveDirectory(delFolder.c_str());
  107. }
  108. DWORD versionNum = 0, versionLen = 0, readLen = 0;
  109. WCHAR versionStr[32] = { 0 };
  110. bool update() {
  111. writeLog(L"Update started..");
  112. wstring updDir = L"tupdates\\temp", readyFilePath = L"tupdates\\temp\\ready", tdataDir = L"tupdates\\temp\\tdata";
  113. {
  114. HANDLE readyFile = CreateFile(readyFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  115. if (readyFile != INVALID_HANDLE_VALUE) {
  116. CloseHandle(readyFile);
  117. } else {
  118. updDir = L"tupdates\\ready"; // old
  119. tdataDir = L"tupdates\\ready\\tdata";
  120. }
  121. }
  122. HANDLE versionFile = CreateFile((tdataDir + L"\\version").c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  123. if (versionFile != INVALID_HANDLE_VALUE) {
  124. if (!ReadFile(versionFile, &versionNum, sizeof(DWORD), &readLen, NULL) || readLen != sizeof(DWORD)) {
  125. versionNum = 0;
  126. } else {
  127. if (versionNum == 0x7FFFFFFF) { // alpha version
  128. } else if (!ReadFile(versionFile, &versionLen, sizeof(DWORD), &readLen, NULL) || readLen != sizeof(DWORD) || versionLen > 63) {
  129. versionNum = 0;
  130. } else if (!ReadFile(versionFile, versionStr, versionLen, &readLen, NULL) || readLen != versionLen) {
  131. versionNum = 0;
  132. }
  133. }
  134. CloseHandle(versionFile);
  135. writeLog(L"Version file read.");
  136. } else {
  137. writeLog(L"Could not open version file to update registry :(");
  138. }
  139. deque<wstring> dirs;
  140. dirs.push_back(updDir);
  141. deque<wstring> from, to, forcedirs;
  142. do {
  143. wstring dir = dirs.front();
  144. dirs.pop_front();
  145. wstring toDir = updateTo;
  146. if (dir.size() > updDir.size() + 1) {
  147. toDir += (dir.substr(updDir.size() + 1) + L"\\");
  148. forcedirs.push_back(toDir);
  149. writeLog(L"Parsing dir '" + toDir + L"' in update tree..");
  150. }
  151. WIN32_FIND_DATA findData;
  152. HANDLE findHandle = FindFirstFileEx((dir + L"\\*").c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, 0, 0);
  153. if (findHandle == INVALID_HANDLE_VALUE) {
  154. DWORD errorCode = GetLastError();
  155. if (errorCode == ERROR_PATH_NOT_FOUND) { // no update is ready
  156. return true;
  157. }
  158. writeLog(L"Error: failed to find update files :(");
  159. updateError(L"Failed to find update files", errorCode);
  160. delFolder();
  161. return false;
  162. }
  163. do {
  164. wstring fname = dir + L"\\" + findData.cFileName;
  165. if (fname.substr(0, tdataDir.size()) == tdataDir && (fname.size() <= tdataDir.size() || fname.at(tdataDir.size()) == '/')) {
  166. writeLog(L"Skipped 'tdata' path '" + fname + L"'");
  167. } else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  168. if (findData.cFileName != wstring(L".") && findData.cFileName != wstring(L"..")) {
  169. dirs.push_back(fname);
  170. writeLog(L"Added dir '" + fname + L"' in update tree..");
  171. }
  172. } else {
  173. wstring tofname = updateTo + fname.substr(updDir.size() + 1);
  174. if (equal(tofname, updaterName)) { // bad update - has Updater.exe - delete all dir
  175. writeLog(L"Error: bad update, has Updater.exe! '" + tofname + L"' equal '" + updaterName + L"'");
  176. delFolder();
  177. return false;
  178. } else if (equal(tofname, updateTo + L"Telegram.exe") && exeName != L"Telegram.exe") {
  179. wstring fullBinaryPath = updateTo + exeName;
  180. writeLog(L"Target binary found: '" + tofname + L"', changing to '" + fullBinaryPath + L"'");
  181. tofname = fullBinaryPath;
  182. }
  183. if (equal(fname, readyFilePath)) {
  184. writeLog(L"Skipped ready file '" + fname + L"'");
  185. } else {
  186. from.push_back(fname);
  187. to.push_back(tofname);
  188. writeLog(L"Added file '" + fname + L"' to be copied to '" + tofname + L"'");
  189. }
  190. }
  191. } while (FindNextFile(findHandle, &findData));
  192. DWORD errorCode = GetLastError();
  193. if (errorCode && errorCode != ERROR_NO_MORE_FILES) { // everything is found
  194. writeLog(L"Error: failed to find next update file :(");
  195. updateError(L"Failed to find next update file", errorCode);
  196. delFolder();
  197. return false;
  198. }
  199. FindClose(findHandle);
  200. } while (!dirs.empty());
  201. for (size_t i = 0; i < forcedirs.size(); ++i) {
  202. wstring forcedir = forcedirs[i];
  203. writeLog(L"Forcing dir '" + forcedir + L"'..");
  204. if (!forcedir.empty() && !CreateDirectory(forcedir.c_str(), NULL)) {
  205. DWORD errorCode = GetLastError();
  206. if (errorCode && errorCode != ERROR_ALREADY_EXISTS) {
  207. writeLog(L"Error: failed to create dir '" + forcedir + L"'..");
  208. updateError(L"Failed to create directory", errorCode);
  209. delFolder();
  210. return false;
  211. }
  212. writeLog(L"Already exists!");
  213. }
  214. }
  215. for (size_t i = 0; i < from.size(); ++i) {
  216. wstring fname = from[i], tofname = to[i];
  217. BOOL copyResult;
  218. do {
  219. writeLog(L"Copying file '" + fname + L"' to '" + tofname + L"'..");
  220. int copyTries = 0;
  221. do {
  222. copyResult = CopyFile(fname.c_str(), tofname.c_str(), FALSE);
  223. if (!copyResult) {
  224. ++copyTries;
  225. Sleep(100);
  226. } else {
  227. break;
  228. }
  229. } while (copyTries < 100);
  230. if (!copyResult) {
  231. writeLog(L"Error: failed to copy, asking to retry..");
  232. WCHAR errMsg[2048];
  233. wsprintf(errMsg, L"Failed to update Telegram :(\n%s is not accessible.", tofname.c_str());
  234. if (MessageBox(0, errMsg, L"Update error!", MB_ICONERROR | MB_RETRYCANCEL) != IDRETRY) {
  235. delFolder();
  236. return false;
  237. }
  238. }
  239. } while (!copyResult);
  240. }
  241. writeLog(L"Update succeed! Clearing folder..");
  242. delFolder();
  243. return true;
  244. }
  245. void updateRegistry() {
  246. if (versionNum && versionNum != 0x7FFFFFFF) {
  247. writeLog(L"Updating registry..");
  248. versionStr[versionLen / 2] = 0;
  249. HKEY rkey;
  250. LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1", 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &rkey);
  251. if (status == ERROR_SUCCESS) {
  252. writeLog(L"Checking registry install location..");
  253. static const int bufSize = 4096;
  254. DWORD locationType, locationSize = bufSize * 2;
  255. WCHAR locationStr[bufSize], exp[bufSize];
  256. if (RegQueryValueEx(rkey, L"InstallLocation", 0, &locationType, (BYTE*)locationStr, &locationSize) == ERROR_SUCCESS) {
  257. locationSize /= 2;
  258. if (locationStr[locationSize - 1]) {
  259. locationStr[locationSize++] = 0;
  260. }
  261. if (locationType == REG_EXPAND_SZ) {
  262. DWORD copy = ExpandEnvironmentStrings(locationStr, exp, bufSize);
  263. if (copy <= bufSize) {
  264. memcpy(locationStr, exp, copy * sizeof(WCHAR));
  265. }
  266. }
  267. if (locationType == REG_EXPAND_SZ || locationType == REG_SZ) {
  268. if (PathCanonicalize(exp, locationStr)) {
  269. memcpy(locationStr, exp, bufSize * sizeof(WCHAR));
  270. if (GetFullPathName(L".", bufSize, exp, 0) < bufSize) {
  271. wstring installpath = locationStr, mypath = exp;
  272. if (installpath == mypath + L"\\" || true) { // always update reg info, if we found it
  273. WCHAR nameStr[bufSize], dateStr[bufSize], publisherStr[bufSize], icongroupStr[bufSize];
  274. SYSTEMTIME stLocalTime;
  275. GetLocalTime(&stLocalTime);
  276. RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (const BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
  277. wsprintf(nameStr, L"Telegram Desktop");
  278. RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (const BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
  279. wsprintf(publisherStr, L"Telegram FZ-LLC");
  280. RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (const BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
  281. wsprintf(icongroupStr, L"Telegram Desktop");
  282. RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (const BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));
  283. wsprintf(dateStr, L"%04d%02d%02d", stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay);
  284. RegSetValueEx(rkey, L"InstallDate", 0, REG_SZ, (const BYTE*)dateStr, (wcslen(dateStr) + 1) * sizeof(WCHAR));
  285. const WCHAR *appURL = L"https://desktop.telegram.org";
  286. RegSetValueEx(rkey, L"HelpLink", 0, REG_SZ, (const BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
  287. RegSetValueEx(rkey, L"URLInfoAbout", 0, REG_SZ, (const BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
  288. RegSetValueEx(rkey, L"URLUpdateInfo", 0, REG_SZ, (const BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
  289. }
  290. }
  291. }
  292. }
  293. }
  294. RegCloseKey(rkey);
  295. }
  296. }
  297. }
  298. int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdParamarg, int cmdShow) {
  299. base::Platform::InitDynamicLibraries();
  300. openLog();
  301. _oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
  302. // CAPIHook apiHook("kernel32.dll", "SetUnhandledExceptionFilter", (PROC)RedirectedSetUnhandledExceptionFilter);
  303. writeLog(L"Updaters started..");
  304. LPWSTR *args;
  305. int argsCount;
  306. bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false;
  307. args = CommandLineToArgvW(GetCommandLine(), &argsCount);
  308. if (args) {
  309. for (int i = 1; i < argsCount; ++i) {
  310. writeLog(std::wstring(L"Argument: ") + args[i]);
  311. if (equal(args[i], L"-update")) {
  312. needupdate = true;
  313. } else if (equal(args[i], L"-autostart")) {
  314. autostart = true;
  315. } else if (equal(args[i], L"-debug")) {
  316. debug = _debug = true;
  317. openLog();
  318. } else if (equal(args[i], L"-startintray")) {
  319. startintray = true;
  320. } else if (equal(args[i], L"-writeprotected") && ++i < argsCount) {
  321. writeLog(std::wstring(L"Argument: ") + args[i]);
  322. writeprotected = true;
  323. updateTo = args[i];
  324. for (int j = 0, l = updateTo.size(); j < l; ++j) {
  325. if (updateTo[j] == L'/') {
  326. updateTo[j] = L'\\';
  327. }
  328. }
  329. } else if (equal(args[i], L"-workdir") && ++i < argsCount) {
  330. writeLog(std::wstring(L"Argument: ") + args[i]);
  331. customWorkingDir = args[i];
  332. } else if (equal(args[i], L"-key") && ++i < argsCount) {
  333. writeLog(std::wstring(L"Argument: ") + args[i]);
  334. customKeyFile = args[i];
  335. } else if (equal(args[i], L"-exename") && ++i < argsCount) {
  336. writeLog(std::wstring(L"Argument: ") + args[i]);
  337. exeName = args[i];
  338. for (int j = 0, l = exeName.size(); j < l; ++j) {
  339. if (exeName[j] == L'/' || exeName[j] == L'\\') {
  340. exeName = L"Telegram.exe";
  341. break;
  342. }
  343. }
  344. }
  345. }
  346. if (exeName.empty()) {
  347. exeName = L"Telegram.exe";
  348. }
  349. if (needupdate) writeLog(L"Need to update!");
  350. if (autostart) writeLog(L"From autostart!");
  351. if (writeprotected) writeLog(L"Write Protected folder!");
  352. if (!customWorkingDir.empty()) writeLog(L"Will pass custom working dir: " + customWorkingDir);
  353. updaterName = args[0];
  354. writeLog(L"Updater name is: " + updaterName);
  355. if (updaterName.size() > 11) {
  356. if (equal(updaterName.substr(updaterName.size() - 11), L"Updater.exe")) {
  357. updaterDir = updaterName.substr(0, updaterName.size() - 11);
  358. writeLog(L"Updater dir is: " + updaterDir);
  359. if (!writeprotected) {
  360. updateTo = updaterDir;
  361. }
  362. writeLog(L"Update to: " + updateTo);
  363. if (needupdate && update()) {
  364. updateRegistry();
  365. }
  366. if (writeprotected) { // if we can't clear all tupdates\ready (Updater.exe is there) - clear only version
  367. if (DeleteFile(L"tupdates\\temp\\tdata\\version") || DeleteFile(L"tupdates\\ready\\tdata\\version")) {
  368. writeLog(L"Version file deleted!");
  369. } else {
  370. writeLog(L"Error: could not delete version file");
  371. }
  372. }
  373. } else {
  374. writeLog(L"Error: bad exe name!");
  375. }
  376. } else {
  377. writeLog(L"Error: short exe name!");
  378. }
  379. LocalFree(args);
  380. } else {
  381. writeLog(L"Error: No command line arguments!");
  382. }
  383. wstring targs;
  384. if (autostart) targs += L" -autostart";
  385. if (debug) targs += L" -debug";
  386. if (startintray) targs += L" -startintray";
  387. if (!customWorkingDir.empty()) {
  388. targs += L" -workdir \"" + customWorkingDir + L"\"";
  389. }
  390. if (!customKeyFile.empty()) {
  391. targs += L" -key \"" + customKeyFile + L"\"";
  392. }
  393. writeLog(L"Result arguments: " + targs);
  394. bool executed = false;
  395. if (writeprotected) { // run un-elevated
  396. writeLog(L"Trying to run un-elevated by temp.lnk");
  397. HRESULT hres = CoInitialize(0);
  398. if (SUCCEEDED(hres)) {
  399. IShellLink* psl;
  400. HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
  401. if (SUCCEEDED(hres)) {
  402. IPersistFile* ppf;
  403. wstring exe = updateTo + exeName, dir = updateTo;
  404. psl->SetArguments((targs.size() ? targs.substr(1) : targs).c_str());
  405. psl->SetPath(exe.c_str());
  406. psl->SetWorkingDirectory(dir.c_str());
  407. psl->SetDescription(L"");
  408. hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
  409. if (SUCCEEDED(hres)) {
  410. wstring lnk = L"tupdates\\temp\\temp.lnk";
  411. hres = ppf->Save(lnk.c_str(), TRUE);
  412. if (!SUCCEEDED(hres)) {
  413. lnk = L"tupdates\\ready\\temp.lnk"; // old
  414. hres = ppf->Save(lnk.c_str(), TRUE);
  415. }
  416. ppf->Release();
  417. if (SUCCEEDED(hres)) {
  418. writeLog(L"Executing un-elevated through link..");
  419. ShellExecute(0, 0, L"explorer.exe", lnk.c_str(), 0, SW_SHOWNORMAL);
  420. executed = true;
  421. } else {
  422. writeLog(L"Error: ppf->Save failed");
  423. }
  424. } else {
  425. writeLog(L"Error: Could not create interface IID_IPersistFile");
  426. }
  427. psl->Release();
  428. } else {
  429. writeLog(L"Error: could not create instance of IID_IShellLink");
  430. }
  431. CoUninitialize();
  432. } else {
  433. writeLog(L"Error: Could not initialize COM");
  434. }
  435. }
  436. if (!executed) {
  437. ShellExecute(0, 0, (updateTo + exeName).c_str(), (L"-noupdate" + targs).c_str(), 0, SW_SHOWNORMAL);
  438. }
  439. writeLog(L"Executed '" + exeName + L"', closing log and quitting..");
  440. closeLog();
  441. return 0;
  442. }
  443. static const WCHAR *_programName = L"Telegram Desktop"; // folder in APPDATA, if current path is unavailable for writing
  444. static const WCHAR *_exeName = L"Updater.exe";
  445. LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter = 0;
  446. typedef BOOL (FAR STDAPICALLTYPE *t_miniDumpWriteDump)(
  447. _In_ HANDLE hProcess,
  448. _In_ DWORD ProcessId,
  449. _In_ HANDLE hFile,
  450. _In_ MINIDUMP_TYPE DumpType,
  451. _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  452. _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  453. _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  454. );
  455. t_miniDumpWriteDump miniDumpWriteDump = 0;
  456. HANDLE _generateDumpFileAtPath(const WCHAR *path) {
  457. static const int maxFileLen = MAX_PATH * 10;
  458. WCHAR szPath[maxFileLen];
  459. wsprintf(szPath, L"%stdata\\", path);
  460. if (!CreateDirectory(szPath, NULL)) {
  461. if (GetLastError() != ERROR_ALREADY_EXISTS) {
  462. return 0;
  463. }
  464. }
  465. wsprintf(szPath, L"%sdumps\\", path);
  466. if (!CreateDirectory(szPath, NULL)) {
  467. if (GetLastError() != ERROR_ALREADY_EXISTS) {
  468. return 0;
  469. }
  470. }
  471. WCHAR szFileName[maxFileLen];
  472. WCHAR szExeName[maxFileLen];
  473. wcscpy_s(szExeName, _exeName);
  474. WCHAR *dotFrom = wcschr(szExeName, WCHAR(L'.'));
  475. if (dotFrom) {
  476. wsprintf(dotFrom, L"");
  477. }
  478. SYSTEMTIME stLocalTime;
  479. GetLocalTime(&stLocalTime);
  480. wsprintf(
  481. szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
  482. szPath, szExeName, updaterVersionStr,
  483. stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
  484. stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
  485. GetCurrentProcessId(), GetCurrentThreadId());
  486. return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
  487. }
  488. void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
  489. static const int maxFileLen = MAX_PATH * 10;
  490. closeLog();
  491. HMODULE hDll = LoadLibrary(L"DBGHELP.DLL");
  492. if (!hDll) return;
  493. miniDumpWriteDump = (t_miniDumpWriteDump)GetProcAddress(hDll, "MiniDumpWriteDump");
  494. if (!miniDumpWriteDump) return;
  495. HANDLE hDumpFile = 0;
  496. WCHAR szPath[maxFileLen];
  497. DWORD len = GetModuleFileName(GetModuleHandle(0), szPath, maxFileLen);
  498. if (!len) return;
  499. WCHAR *pathEnd = szPath + len;
  500. if (!_wcsicmp(pathEnd - wcslen(_exeName), _exeName)) {
  501. wsprintf(pathEnd - wcslen(_exeName), L"");
  502. hDumpFile = _generateDumpFileAtPath(szPath);
  503. }
  504. if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
  505. WCHAR wstrPath[maxFileLen];
  506. DWORD wstrPathLen = GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen);
  507. if (wstrPathLen) {
  508. wsprintf(wstrPath + wstrPathLen, L"\\%s\\", _programName);
  509. hDumpFile = _generateDumpFileAtPath(wstrPath);
  510. }
  511. }
  512. if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
  513. return;
  514. }
  515. MINIDUMP_EXCEPTION_INFORMATION ExpParam = {0};
  516. ExpParam.ThreadId = GetCurrentThreadId();
  517. ExpParam.ExceptionPointers = pExceptionPointers;
  518. ExpParam.ClientPointers = TRUE;
  519. miniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);
  520. }
  521. LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) {
  522. _generateDump(pExceptionPointers);
  523. return _oldWndExceptionFilter ? (*_oldWndExceptionFilter)(pExceptionPointers) : EXCEPTION_CONTINUE_SEARCH;
  524. }
  525. // see http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li
  526. LPTOP_LEVEL_EXCEPTION_FILTER WINAPI RedirectedSetUnhandledExceptionFilter(_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
  527. // When the CRT calls SetUnhandledExceptionFilter with NULL parameter
  528. // our handler will not get removed.
  529. _oldWndExceptionFilter = lpTopLevelExceptionFilter;
  530. return 0;
  531. }