diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0a34cbb..6fdca1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -102,16 +102,16 @@ jobs: if ls build/Release/ep_taskbar.*.amd64.dll 1> /dev/null 2>&1; then mkdir -p build/Release/x64 for file in build/Release/ep_taskbar.*.amd64.dll; do - cp "$file" "build/Release/x64/$(basename "$file" .amd64.dll).dll" + mv "$file" "build/Release/x64/$(basename "$file" .amd64.dll).dll" done fi - # if ls build/Release/ep_taskbar.*.arm64.dll 1> /dev/null 2>&1; then - # mkdir -p build/Release/ARM64 - # for file in build/Release/ep_taskbar.*.arm64.dll; do - # cp "$file" "build/Release/ARM64/$(basename "$file" .arm64.dll).dll" - # done - # fi + if ls build/Release/ep_taskbar.*.arm64.dll 1> /dev/null 2>&1; then + mkdir -p build/Release/ARM64 + for file in build/Release/ep_taskbar.*.arm64.dll; do + mv "$file" "build/Release/ARM64/$(basename "$file" .arm64.dll).dll" + done + fi - name: Build funchook amd64 shell: powershell diff --git a/ExplorerPatcher/ExplorerPatcher.vcxproj b/ExplorerPatcher/ExplorerPatcher.vcxproj index f0a3c87..4151649 100644 --- a/ExplorerPatcher/ExplorerPatcher.vcxproj +++ b/ExplorerPatcher/ExplorerPatcher.vcxproj @@ -257,7 +257,7 @@ true - + true diff --git a/ExplorerPatcher/def.h b/ExplorerPatcher/def.h index ea27618..17c2a38 100644 --- a/ExplorerPatcher/def.h +++ b/ExplorerPatcher/def.h @@ -17,7 +17,7 @@ #else #define SETUP_UTILITY_NAME "ep_setup.arm64.exe" #endif -#define TOAST_BUFSIZ 1024 +#define TOAST_BUFSIZ 1536 #define SEH_REGPATH "Control Panel\\Quick Actions\\Control Center\\QuickActionsStateCapture\\ExplorerPatcher" #define EP_SETUP_HELPER_SWITCH "/CreateExplorerShellUnelevatedAfterServicing" #define EP_DWM_SERVICENAME "ep_dwm_" EP_CLSID_LITE diff --git a/ExplorerPatcher/dllmain.c b/ExplorerPatcher/dllmain.c index e929091..6a86887 100644 --- a/ExplorerPatcher/dllmain.c +++ b/ExplorerPatcher/dllmain.c @@ -246,197 +246,6 @@ HRESULT WINAPI _DllGetClassObject( DEFINE_GUID(CLSID_EPStart10, 0x9717d01, 0x5d10, 0x4fb5, 0xbd, 0x5, 0x46, 0x38, 0xb, 0x51, 0x65, 0xaa); -#pragma region "Updates" -#if WITH_MAIN_PATCHER -DWORD CheckForUpdatesThread(LPVOID timeout) -{ - HRESULT hr = S_OK; - HSTRING_HEADER header_AppIdHString; - HSTRING AppIdHString = NULL; - HSTRING_HEADER header_ToastNotificationManagerHString; - HSTRING ToastNotificationManagerHString = NULL; - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationManagerStatics* toastStatics = NULL; - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier = NULL; - HSTRING_HEADER header_ToastNotificationHString; - HSTRING ToastNotificationHString = NULL; - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory = NULL; - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification* toast = NULL; - - while (TRUE) - { - HWND hShell_TrayWnd = FindWindowExW( - NULL, - NULL, - L"Shell_TrayWnd", - NULL - ); - if (hShell_TrayWnd) - { - Sleep(timeout); - break; - } - Sleep(100); - } - printf("[Updates] Starting daemon.\n"); - - if (SUCCEEDED(hr)) - { - hr = RoInitialize(RO_INIT_MULTITHREADED); - } - if (SUCCEEDED(hr)) - { - hr = WindowsCreateStringReference( - APPID, - (UINT32)(sizeof(APPID) / sizeof(TCHAR) - 1), - &header_AppIdHString, - &AppIdHString - ); - } - if (SUCCEEDED(hr)) - { - hr = WindowsCreateStringReference( - RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, - (UINT32)(sizeof(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager) / sizeof(wchar_t) - 1), - &header_ToastNotificationManagerHString, - &ToastNotificationManagerHString - ); - } - if (SUCCEEDED(hr)) - { - hr = RoGetActivationFactory( - ToastNotificationManagerHString, - &UIID_IToastNotificationManagerStatics, - (LPVOID*)&toastStatics - ); - } - if (SUCCEEDED(hr)) - { - hr = toastStatics->lpVtbl->CreateToastNotifierWithId( - toastStatics, - AppIdHString, - ¬ifier - ); - } - if (SUCCEEDED(hr)) - { - hr = WindowsCreateStringReference( - RuntimeClass_Windows_UI_Notifications_ToastNotification, - (UINT32)(sizeof(RuntimeClass_Windows_UI_Notifications_ToastNotification) / sizeof(wchar_t) - 1), - &header_ToastNotificationHString, - &ToastNotificationHString - ); - } - if (SUCCEEDED(hr)) - { - hr = RoGetActivationFactory( - ToastNotificationHString, - &UIID_IToastNotificationFactory, - (LPVOID*)¬ifFactory - ); - } - - HANDLE hEvents[2]; - hEvents[0] = CreateEventW(NULL, FALSE, FALSE, L"EP_Ev_CheckForUpdates_" _T(EP_CLSID)); - hEvents[1] = CreateEventW(NULL, FALSE, FALSE, L"EP_Ev_InstallUpdates_" _T(EP_CLSID)); - if (hEvents[0] && hEvents[1]) - { - if (bShowUpdateToast) - { - ShowUpdateSuccessNotification(hModule, notifier, notifFactory, &toast); - - HKEY hKey = NULL; - - RegCreateKeyExW( - HKEY_CURRENT_USER, - TEXT(REGPATH), - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_READ | KEY_WOW64_64KEY | KEY_WRITE, - NULL, - &hKey, - NULL - ); - if (hKey == NULL || hKey == INVALID_HANDLE_VALUE) - { - hKey = NULL; - } - if (hKey) - { - bShowUpdateToast = FALSE; - RegSetValueExW( - hKey, - TEXT("IsUpdatePending"), - 0, - REG_DWORD, - &bShowUpdateToast, - sizeof(DWORD) - ); - RegCloseKey(hKey); - } - } - if (dwUpdatePolicy != UPDATE_POLICY_MANUAL) - { - InstallUpdatesIfAvailable(hModule, notifier, notifFactory, &toast, UPDATES_OP_DEFAULT, bAllocConsole, dwUpdatePolicy); - } - DWORD dwRet = 0; - while (TRUE) - { - switch (WaitForMultipleObjects(2, hEvents, FALSE, INFINITE)) - { - case WAIT_OBJECT_0: - { - InstallUpdatesIfAvailable(hModule, notifier, notifFactory, &toast, UPDATES_OP_CHECK, bAllocConsole, dwUpdatePolicy); - break; - } - case WAIT_OBJECT_0 + 1: - { - InstallUpdatesIfAvailable(hModule, notifier, notifFactory, &toast, UPDATES_OP_INSTALL, bAllocConsole, dwUpdatePolicy); - break; - } - default: - { - break; - } - } - } - CloseHandle(hEvents[0]); - CloseHandle(hEvents[1]); - } - - if (toast) - { - toast->lpVtbl->Release(toast); - } - if (notifFactory) - { - notifFactory->lpVtbl->Release(notifFactory); - } - if (ToastNotificationHString) - { - WindowsDeleteString(ToastNotificationHString); - } - if (notifier) - { - notifier->lpVtbl->Release(notifier); - } - if (toastStatics) - { - toastStatics->lpVtbl->Release(toastStatics); - } - if (ToastNotificationManagerHString) - { - WindowsDeleteString(ToastNotificationManagerHString); - } - if (AppIdHString) - { - WindowsDeleteString(AppIdHString); - } -} -#endif -#pragma endregion - - #pragma region "Generics" #if WITH_MAIN_PATCHER HWND GetMonitorInfoFromPointForTaskbarFlyoutActivation(POINT ptCursor, DWORD dwFlags, LPMONITORINFO lpMi) diff --git a/ExplorerPatcher/updates.c b/ExplorerPatcher/updates.c deleted file mode 100644 index 48a796d..0000000 --- a/ExplorerPatcher/updates.c +++ /dev/null @@ -1,1245 +0,0 @@ -#include "updates.h" - -BOOL IsUpdatePolicy(LPCWSTR wszDataStore, DWORD dwUpdatePolicy) -{ - HKEY hKey = NULL; - DWORD dwSize = 0; - DWORD dwQueriedPolicy = 0; - BOOL bIsPolicyMatch = (dwUpdatePolicy == UPDATE_POLICY_AUTO); - - RegCreateKeyExW( - HKEY_CURRENT_USER, - wszDataStore, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_READ | KEY_WOW64_64KEY, - NULL, - &hKey, - NULL - ); - if (hKey == NULL || hKey == INVALID_HANDLE_VALUE) - { - hKey = NULL; - } - if (hKey) - { - dwSize = sizeof(DWORD); - RegQueryValueExW( - hKey, - TEXT("UpdatePolicy"), - 0, - NULL, - &dwQueriedPolicy, - &dwSize - ); - RegCloseKey(hKey); - bIsPolicyMatch = (dwQueriedPolicy == dwUpdatePolicy); - } - return bIsPolicyMatch; -} - -void IsUpdateAvailableHelperCallback( - HINTERNET hInternet, - struct IsUpdateAvailableParameters* params, - DWORD dwInternetStatus, - INTERNET_ASYNC_RESULT* lpvStatusInformation, - DWORD dwStatusInformationLength -) -{ - if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE) - { - params->hInternet = lpvStatusInformation->dwResult; - SetEvent(params->hEvent); - } -} - -BOOL IsUpdateAvailableHelper( - char* url, - char* szCheckAgainst, - DWORD dwUpdateTimeout, - BOOL* lpFail, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification** toast, - BOOL bUpdatePreferStaging, - WCHAR* wszInfoURL, - DWORD dwInfoURLLen, - HMODULE hModule, - DWORD* pLeftMost, DWORD* pSecondLeft, DWORD* pSecondRight, DWORD* pRightMost -) -{ - BOOL bIsUpdateAvailable = FALSE; - - struct IsUpdateAvailableParameters params; - params.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!params.hEvent) - { - return bIsUpdateAvailable; - } - - char* staging_buffer = NULL; - HINTERNET hInternet = NULL; - - if (bUpdatePreferStaging) - { - if (hInternet = InternetOpenA( - UPDATES_USER_AGENT, - INTERNET_OPEN_TYPE_PRECONFIG, - NULL, - NULL, - 0 - )) - { - HINTERNET hConnect = InternetOpenUrlA( - hInternet, - url, - NULL, - 0, - INTERNET_FLAG_RAW_DATA | - INTERNET_FLAG_RELOAD | - INTERNET_FLAG_RESYNCHRONIZE | - INTERNET_FLAG_NO_COOKIES | - INTERNET_FLAG_NO_UI | - INTERNET_FLAG_NO_CACHE_WRITE | - INTERNET_FLAG_DONT_CACHE, - ¶ms - ); - if (hConnect) - { - DWORD dwSize = 5000; - DWORD dwRead = dwSize; - staging_buffer = calloc(dwSize, sizeof(char)); - if (staging_buffer) - { - BOOL bRet = FALSE; - if (bRet = InternetReadFile( - hConnect, - staging_buffer, - dwSize - 1, - &dwRead - )) - { - char* a1 = strstr(staging_buffer, "\"browser_download_url\""); - if (a1) - { - char* a2 = strchr(a1 + 24, '"'); - if (a2) - { - a2[0] = 0; - printf("[Updates] Prerelease update URL: \"%s\"\n", a1 + 24); - url = a1 + 24; - bUpdatePreferStaging = FALSE; - if (wszInfoURL) - { - char* a3 = strstr(staging_buffer, "\"html_url\""); - if (a3) - { - char* a4 = strchr(a3 + 12, '"'); - if (a4) - { - a4[0] = 0; - printf("[Updates] Release notes URL: \"%s\"\n", a3 + 12); - MultiByteToWideChar( - CP_UTF8, - MB_PRECOMPOSED, - a3 + 12, - -1, - wszInfoURL, - dwInfoURLLen - ); - } - } - } - } - } - } - } - InternetCloseHandle(hConnect); - } - InternetCloseHandle(hInternet); - } - } - - if (!bUpdatePreferStaging && (hInternet = InternetOpenA( - UPDATES_USER_AGENT, - INTERNET_OPEN_TYPE_PRECONFIG, - NULL, - NULL, - 0 - ))) - { - HINTERNET hConnect = InternetOpenUrlA( - hInternet, - url, - NULL, - 0, - INTERNET_FLAG_RAW_DATA | - INTERNET_FLAG_RELOAD | - INTERNET_FLAG_RESYNCHRONIZE | - INTERNET_FLAG_NO_COOKIES | - INTERNET_FLAG_NO_UI | - INTERNET_FLAG_NO_CACHE_WRITE | - INTERNET_FLAG_DONT_CACHE, - ¶ms - ); - if (hConnect) - { - if (szCheckAgainst) - { - BOOL bRet = FALSE; - DWORD dwRead = 0; - char hash[DOSMODE_OFFSET + UPDATES_HASH_SIZE + 1]; - ZeroMemory(hash, DOSMODE_OFFSET + UPDATES_HASH_SIZE + 1); - if (bRet = InternetReadFile( - hConnect, - hash, - DOSMODE_OFFSET + UPDATES_HASH_SIZE, - &dwRead - ) && dwRead == DOSMODE_OFFSET + UPDATES_HASH_SIZE) - { -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Hash of remote file is \"%s\" (%s).\n", DOSMODE_OFFSET + hash, (hash[0] == 0x4D && hash[1] == 0x5A) ? "valid" : "invalid"); -#endif - BOOL bOldType = TRUE; - char *szLeftMost = NULL, *szSecondLeft = NULL, *szSecondRight = NULL, *szRightMost = NULL, *szRealHash = NULL; - if (hModule) - { - if (hash[0] == 0x4D && hash[1] == 0x5A) - { - if (strchr(DOSMODE_OFFSET + hash, '.')) - { - szLeftMost = DOSMODE_OFFSET + hash; - if (szSecondLeft = strchr(szLeftMost, '.')) - { - *szSecondLeft = 0; - szSecondLeft++; - if (szSecondRight = strchr(szSecondLeft, '.')) - { - *szSecondRight = 0; - szSecondRight++; - if (szRightMost = strchr(szSecondRight, '.')) - { - *szRightMost = 0; - szRightMost++; - if (szRealHash = strchr(szRightMost, '.')) - { - bOldType = FALSE; - - *szRealHash = 0; - szRealHash++; - DWORD dwRemoteLeftMost = atoi(szLeftMost); - if (pLeftMost) *pLeftMost = dwRemoteLeftMost - ((dwRemoteLeftMost == 22622 && szRealHash[0] != '!') ? 1 : 0); - DWORD dwRemoteSecondLeft = atoi(szSecondLeft); - if (pSecondLeft) *pSecondLeft = dwRemoteSecondLeft; - DWORD dwRemoteSecondRight = atoi(szSecondRight); - if (pSecondRight) *pSecondRight = dwRemoteSecondRight; - DWORD dwRemoteRightMost = atoi(szRightMost); - if (pRightMost) *pRightMost = dwRemoteRightMost; - DWORD dwLocalLeftMost = 0; - DWORD dwLocalSecondLeft = 0; - DWORD dwLocalSecondRight = 0; - DWORD dwLocalRightMost = 0; - BOOL bExtractedFromHash = FALSE; - CHAR hashCopy[100]; - strcpy_s(hashCopy, 100, szCheckAgainst); - char* szLocalLeftMost = NULL, *szLocalSecondLeft = NULL, *szLocalSecondRight = NULL, *szLocalRightMost = NULL, *szLocalRealHash = NULL; - if (strchr(hashCopy, '.')) - { - szLocalLeftMost = hashCopy; - if (szLocalSecondLeft = strchr(szLocalLeftMost, '.')) - { - *szLocalSecondLeft = 0; - szLocalSecondLeft++; - if (szLocalSecondRight = strchr(szLocalSecondLeft, '.')) - { - *szLocalSecondRight = 0; - szLocalSecondRight++; - if (szLocalRightMost = strchr(szLocalSecondRight, '.')) - { - *szLocalRightMost = 0; - szLocalRightMost++; - if (szLocalRealHash = strchr(szLocalRightMost, '.')) - { - *szLocalRealHash = 0; - szLocalRealHash++; - - bExtractedFromHash = TRUE; - dwLocalLeftMost = atoi(szLocalLeftMost); - dwLocalSecondLeft = atoi(szLocalSecondLeft); - dwLocalSecondRight = atoi(szLocalSecondRight); - dwLocalRightMost = atoi(szLocalRightMost); -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Local version obtained from hash is %d.%d.%d.%d.\n", dwLocalLeftMost, dwLocalSecondLeft, dwLocalSecondRight, dwLocalRightMost); -#endif - } - } - } - } - } - if (!bExtractedFromHash) - { - QueryVersionInfo(hModule, VS_VERSION_INFO, &dwLocalLeftMost, &dwLocalSecondLeft, &dwLocalSecondRight, &dwLocalRightMost); - } - - int res = 0; - if (!res) - { - if (dwLocalLeftMost < dwRemoteLeftMost) - { - res = -1; - } - if (dwLocalLeftMost > dwRemoteLeftMost) - { - res = 1; - } - if (!res) - { - if (dwLocalSecondLeft < dwRemoteSecondLeft) - { - res = -1; - } - if (dwLocalSecondLeft > dwRemoteSecondLeft) - { - res = 1; - } - if (!res) - { - if (dwLocalSecondRight < dwRemoteSecondRight) - { - res = -1; - } - if (dwLocalSecondRight > dwRemoteSecondRight) - { - res = 1; - } - if (!res) - { - if (dwLocalRightMost < dwRemoteRightMost) - { - res = -1; - } - if (dwLocalRightMost > dwRemoteRightMost) - { - res = 1; - } - } - } - } - } - DWORD dwAllowDowngrades = FALSE, dwSize = sizeof(DWORD); - RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"UpdateAllowDowngrades", RRF_RT_DWORD, NULL, &dwAllowDowngrades, &dwSize); - if ((res == 1 && dwAllowDowngrades) || res == -1) - { - bIsUpdateAvailable = TRUE; - } - else if (res == 0) - { - *(szSecondLeft - 1) = '.'; - *(szSecondRight - 1) = '.'; - *(szRightMost - 1) = '.'; - *(szRealHash - 1) = '.'; - if (_stricmp(DOSMODE_OFFSET + hash, szCheckAgainst)) - { - bIsUpdateAvailable = TRUE; - } - } - } - } - } - } - } - } - } - - /*if (bOldType) - { - if (hash[0] == 0x4D && hash[1] == 0x5A && _stricmp(DOSMODE_OFFSET + hash, szCheckAgainst)) - { - bIsUpdateAvailable = TRUE; - } - }*/ - } - else - { -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Failed. Read %d bytes.\n", dwRead); -#endif - if (lpFail) *lpFail = TRUE; - } - } - else - { - WCHAR wszPath[MAX_PATH]; - ZeroMemory(wszPath, MAX_PATH * sizeof(WCHAR)); - SHGetFolderPathW(NULL, SPECIAL_FOLDER_LEGACY, NULL, SHGFP_TYPE_CURRENT, wszPath); - wcscat_s(wszPath, MAX_PATH, _T(APP_RELATIVE_PATH)); - BOOL bRet = CreateDirectoryW(wszPath, NULL); - if (bRet || (!bRet && GetLastError() == ERROR_ALREADY_EXISTS)) - { - wcscat_s(wszPath, MAX_PATH, L"\\Update for " _T(PRODUCT_NAME) L" from "); - WCHAR wszURL[MAX_PATH]; - ZeroMemory(wszURL, MAX_PATH * sizeof(WCHAR)); - MultiByteToWideChar( - CP_UTF8, - MB_PRECOMPOSED, - url, - -1, - wszURL, - MAX_PATH - ); - if (wszURL[97]) - { - wszURL[96] = L'.'; - wszURL[97] = L'.'; - wszURL[98] = L'.'; - wszURL[99] = L'e'; - wszURL[100] = L'x'; - wszURL[101] = L'e'; - wszURL[102] = 0; - } - for (unsigned int i = 0; i < wszURL; ++i) - { - if (!wszURL[i]) - { - break; - } - if (wszURL[i] == L'/') - { - wszURL[i] = L'\u2215'; - } - else if (wszURL[i] == L':') - { - wszURL[i] = L'\ua789'; - } - } - wcscat_s(wszPath, MAX_PATH, wszURL); -#ifdef UPDATES_VERBOSE_OUTPUT - wprintf(L"[Updates] Download path is \"%s\".\n", wszPath); -#endif - - BOOL bRet = DeleteFileW(wszPath); - if (bRet || (!bRet && GetLastError() == ERROR_FILE_NOT_FOUND)) - { - DWORD bIsUsingEpMake = 0, dwSize = sizeof(DWORD); - RegGetValueW(HKEY_CURRENT_USER, TEXT(REGPATH), L"UpdateUseLocal", RRF_RT_DWORD, NULL, &bIsUsingEpMake, &dwSize); - if (bIsUsingEpMake) { - GetSystemDirectoryW(wszPath, MAX_PATH); - wcscat_s(wszPath, MAX_PATH, L"\\WindowsPowerShell\\v1.0\\powershell.exe"); - } - - FILE* f = NULL; - if (!bIsUsingEpMake && !_wfopen_s( - &f, - wszPath, - L"wb" - ) && f) - { - BYTE* buffer = (BYTE*)malloc(UPDATES_BUFSIZ); - if (buffer != NULL) - { - DWORD dwRead = 0; - bRet = FALSE; - while (bRet = InternetReadFile( - hConnect, - buffer, - UPDATES_BUFSIZ, - &dwRead - )) - { - if (dwRead == 0) - { - bIsUpdateAvailable = TRUE; -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Downloaded finished.\n"); -#endif - break; - } -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Downloaded %d bytes.\n", dwRead); -#endif - fwrite( - buffer, - sizeof(BYTE), - dwRead, - f - ); - dwRead = 0; - } - free(buffer); - } - fclose(f); - } - if (bIsUsingEpMake || bIsUpdateAvailable) - { - bIsUpdateAvailable = FALSE; -#ifdef UPDATES_VERBOSE_OUTPUT - printf( - "[Updates] In order to install this update for the product \"" - PRODUCT_NAME - "\", please allow the request.\n" - ); -#endif - - if (*toast) - { - if (notifier) - { - notifier->lpVtbl->Hide(notifier, *toast); - } - (*toast)->lpVtbl->Release((*toast)); - (*toast) = NULL; - } - - BOOL bHasErrored = FALSE; - BOOL bIsUACEnabled = FALSE; - DWORD(*CheckElevationEnabled)(BOOL*) = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CheckElevationEnabled"); - if (CheckElevationEnabled) CheckElevationEnabled(&bIsUACEnabled); - DWORD dwData = FALSE, dwSize = sizeof(DWORD); - RegGetValueW( - HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", - L"ConsentPromptBehaviorAdmin", - RRF_RT_DWORD, - NULL, - &dwData, - &dwSize - ); - LPWSTR wszSID = NULL; - HANDLE hMyToken = NULL; - if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hMyToken)) - { - PTOKEN_USER ptu = NULL; - DWORD dwSize = 0; - if (!GetTokenInformation(hMyToken, TokenUser, NULL, 0, &dwSize) - && ERROR_INSUFFICIENT_BUFFER == GetLastError()) - { - if (NULL != (ptu = (PTOKEN_USER)LocalAlloc(LPTR, dwSize))) - { - if (!GetTokenInformation(hMyToken, TokenUser, ptu, dwSize, &dwSize)) - { - LocalFree((HLOCAL)ptu); - return FALSE; - } - ConvertSidToStringSidW(ptu->User.Sid, &wszSID); - LocalFree((HLOCAL)ptu); - } - } - CloseHandle(hMyToken); - } - size_t lnSID = (wszSID ? wcslen(wszSID) : 0); - DWORD bIsBuiltInAdministratorInApprovalMode = FALSE; - BOOL bIsBuiltInAdministratorAccount = - IsUserAnAdmin() && - lnSID && !_wcsnicmp(wszSID, L"S-1-5-", 6) && wszSID[lnSID - 4] == L'-' && wszSID[lnSID - 3] == L'5' && wszSID[lnSID - 2] == L'0' && wszSID[lnSID - 1] == L'0'; - if (bIsBuiltInAdministratorAccount) - { - DWORD dwSSize = sizeof(DWORD); - RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", L"FilterAdministratorToken", RRF_RT_DWORD, NULL, &bIsBuiltInAdministratorInApprovalMode, &dwSSize); - } - LocalFree(wszSID); - if (!bIsUACEnabled || !dwData || (bIsBuiltInAdministratorAccount && !bIsBuiltInAdministratorInApprovalMode)) - { - WCHAR wszURL2[MAX_PATH]; - ZeroMemory(wszURL2, MAX_PATH * sizeof(WCHAR)); - MultiByteToWideChar( - CP_UTF8, - MB_PRECOMPOSED, - url, - -1, - wszURL2, - MAX_PATH - ); - - WCHAR wszMsg[500]; - wszMsg[0] = 0; - - WCHAR wszMsgFormat[500]; - EP_L10N_ApplyPreferredLanguageForCurrentThread(); - HMODULE hEPGui = LoadGuiModule(); - if (LoadStringW(hEPGui, IDS_UPDATES_PROMPT, wszMsgFormat, ARRAYSIZE(wszMsgFormat))) - { - swprintf_s(wszMsg, ARRAYSIZE(wszMsg), wszMsgFormat, wszURL2); - } - FreeLibrary(hEPGui); - - if (MessageBoxW( - FindWindowW(L"ExplorerPatcher_GUI_" _T(EP_CLSID), NULL), - wszMsg, - _T(PRODUCT_NAME), - MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION - ) == IDNO) - { - bHasErrored = TRUE; - SetLastError(ERROR_CANCELLED); - } - } - - SHELLEXECUTEINFO ShExecInfo = { 0 }; - ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - ShExecInfo.hwnd = NULL; - ShExecInfo.lpVerb = bIsUsingEpMake ? L"open" : L"runas"; - ShExecInfo.lpFile = wszPath; - ShExecInfo.lpParameters = bIsUsingEpMake ? L"iex (irm 'https://raw.githubusercontent.com/valinet/ep_make/master/ep_make_safe.ps1')" : L"/update_silent"; - ShExecInfo.lpDirectory = NULL; - ShExecInfo.nShow = SW_SHOW; - ShExecInfo.hInstApp = NULL; - if (!bHasErrored && ShellExecuteExW(&ShExecInfo) && ShExecInfo.hProcess) - { - WaitForSingleObject(ShExecInfo.hProcess, INFINITE); - DWORD dwExitCode = 0; - if (GetExitCodeProcess(ShExecInfo.hProcess, &dwExitCode) && !dwExitCode) - { - bIsUpdateAvailable = TRUE; -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Update successful, File Explorer will probably restart momentarily.\n"); -#endif - } - else - { - SetLastError(dwExitCode); -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Update failed because the following error has occured: %d.\n", dwExitCode); -#endif - } - CloseHandle(ShExecInfo.hProcess); - } - else - { - DWORD dwError = GetLastError(); - if (dwError == ERROR_CANCELLED) - { -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Update failed because the request was denied.\n"); -#endif - } - else - { -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Update failed because the following error has occured: %d.\n", GetLastError()); -#endif - } - } - } - } - } - } - InternetCloseHandle(hConnect); - } - else - { - if (lpFail) *lpFail = TRUE; - } - InternetCloseHandle(hInternet); - } - - CloseHandle(params.hEvent); - - if (staging_buffer) - { - free(staging_buffer); - staging_buffer = NULL; - } - - return bIsUpdateAvailable; -} - -BOOL IsUpdateAvailable(LPCWSTR wszDataStore, char* szCheckAgainst, BOOL* lpFail, WCHAR* wszInfoURL, DWORD dwInfoURLLen, HMODULE hModule, - DWORD* pLeftMost, DWORD* pSecondLeft, DWORD* pSecondRight, DWORD* pRightMost -) -{ - HKEY hKey = NULL; - DWORD dwSize = 0; - DWORD dwQueriedPolicy = 0; - BOOL bIsPolicyMatch = FALSE; - CHAR szUpdateURL[MAX_PATH]; - ZeroMemory(szUpdateURL, MAX_PATH * sizeof(CHAR)); - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STABLE); -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Checking against hash \"%s\"\n", szCheckAgainst); -#endif - DWORD dwUpdateTimeout = UPDATES_DEFAULT_TIMEOUT; - DWORD bUpdatePreferStaging = FALSE; - - RegCreateKeyExW( - HKEY_CURRENT_USER, - wszDataStore, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_READ | KEY_WOW64_64KEY, - NULL, - &hKey, - NULL - ); - if (hKey == NULL || hKey == INVALID_HANDLE_VALUE) - { - hKey = NULL; - } - if (hKey) - { - dwSize = MAX_PATH; - RegQueryValueExA( - hKey, - "UpdateURL", - 0, - NULL, - szUpdateURL, - &dwSize - ); - if (dwSize == 1 && szUpdateURL[0] == 0) - { - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STABLE); - } - strcat_s(szUpdateURL, MAX_PATH, "/download/"); - strcat_s(szUpdateURL, MAX_PATH, SETUP_UTILITY_NAME); - if (wszInfoURL) - { - dwSize = dwInfoURLLen; - RegQueryValueExW( - hKey, - L"UpdateURL", - 0, - NULL, - wszInfoURL, - &dwSize - ); - if (dwSize == 1 && wszInfoURL[0] == 0) - { - wcscat_s(wszInfoURL, dwInfoURLLen, _T(UPDATES_RELEASE_INFO_URL_STABLE)); - } - } - dwSize = sizeof(DWORD); - RegQueryValueExA( - hKey, - "UpdateTimeout", - 0, - NULL, - &dwUpdateTimeout, - &dwSize - ); - dwSize = sizeof(DWORD); - RegQueryValueExA( - hKey, - "UpdatePreferStaging", - 0, - NULL, - &bUpdatePreferStaging, - &dwSize - ); - if (bUpdatePreferStaging) - { - ZeroMemory(szUpdateURL, MAX_PATH * sizeof(CHAR)); - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STAGING); - dwSize = MAX_PATH; - RegQueryValueExA( - hKey, - "UpdateURLStaging", - 0, - NULL, - szUpdateURL, - &dwSize - ); - if (dwSize == 1 && szUpdateURL[0] == 0) - { - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STAGING); - } - } - RegCloseKey(hKey); - } -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Update URL: %s\n", szUpdateURL); -#endif - return IsUpdateAvailableHelper( - szUpdateURL, - szCheckAgainst, - dwUpdateTimeout, - lpFail, - NULL, NULL, NULL, - bUpdatePreferStaging, - wszInfoURL, - dwInfoURLLen, - hModule, - pLeftMost, pSecondLeft, pSecondRight, pRightMost - ); -} - -BOOL UpdateProduct( - LPCWSTR wszDataStore, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification** toast, - HMODULE hModule -) -{ - HKEY hKey = NULL; - DWORD dwSize = 0; - DWORD dwQueriedPolicy = 0; - BOOL bIsPolicyMatch = FALSE; - CHAR szUpdateURL[MAX_PATH]; - ZeroMemory(szUpdateURL, MAX_PATH * sizeof(CHAR)); - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STABLE); - - DWORD dwUpdateTimeout = UPDATES_DEFAULT_TIMEOUT; - DWORD bUpdatePreferStaging = FALSE; - - RegCreateKeyExW( - HKEY_CURRENT_USER, - wszDataStore, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_READ | KEY_WOW64_64KEY, - NULL, - &hKey, - NULL - ); - if (hKey == NULL || hKey == INVALID_HANDLE_VALUE) - { - hKey = NULL; - } - if (hKey) - { - dwSize = MAX_PATH; - RegQueryValueExA( - hKey, - "UpdateURL", - 0, - NULL, - szUpdateURL, - &dwSize - ); - if (dwSize == 1 && szUpdateURL[0] == 0) - { - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STABLE); - } - strcat_s(szUpdateURL, MAX_PATH, "/download/"); - strcat_s(szUpdateURL, MAX_PATH, SETUP_UTILITY_NAME); - dwSize = sizeof(DWORD); - RegQueryValueExA( - hKey, - "UpdateTimeout", - 0, - NULL, - &dwUpdateTimeout, - &dwSize - ); - dwSize = sizeof(DWORD); - RegQueryValueExA( - hKey, - "UpdatePreferStaging", - 0, - NULL, - &bUpdatePreferStaging, - &dwSize - ); - if (bUpdatePreferStaging) - { - ZeroMemory(szUpdateURL, MAX_PATH * sizeof(CHAR)); - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STAGING); - dwSize = MAX_PATH; - RegQueryValueExA( - hKey, - "UpdateURLStaging", - 0, - NULL, - szUpdateURL, - &dwSize - ); - if (dwSize == 1 && szUpdateURL[0] == 0) - { - strcat_s(szUpdateURL, MAX_PATH, UPDATES_RELEASE_INFO_URL_STAGING); - } - } - RegCloseKey(hKey); - } -#ifdef UPDATES_VERBOSE_OUTPUT - printf("[Updates] Update URL: %s\n", szUpdateURL); -#endif - return IsUpdateAvailableHelper( - szUpdateURL, - NULL, - dwUpdateTimeout, - NULL, - notifier, - notifFactory, - toast, - bUpdatePreferStaging, - NULL, 0, - hModule, - NULL, NULL, NULL, NULL - ); -} - -BOOL ShowUpdateSuccessNotification( - HMODULE hModule, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification** toast -) -{ - EP_L10N_ApplyPreferredLanguageForCurrentThread(); - HMODULE hEPGui = LoadGuiModule(); - - wchar_t buf[TOAST_BUFSIZ]; - DWORD dwLeftMost = 0; - DWORD dwSecondLeft = 0; - DWORD dwSecondRight = 0; - DWORD dwRightMost = 0; - QueryVersionInfo(hModule, VS_VERSION_INFO, &dwLeftMost, &dwSecondLeft, &dwSecondRight, &dwRightMost); - - const wchar_t text[] = - L"\r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n"; - wchar_t title[100]; - wchar_t body[200]; - title[0] = 0; body[0] = 0; - - LoadStringW(hEPGui, IDS_UPDATES_SUCCESS_T, title, ARRAYSIZE(title)); - - wchar_t bodyFormat[200]; - ZeroMemory(bodyFormat, sizeof(bodyFormat)); - if (LoadStringW(hEPGui, IDS_UPDATES_INSTALLEDVER, bodyFormat, ARRAYSIZE(bodyFormat))) - { - swprintf_s(body, ARRAYSIZE(body), bodyFormat, dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); - } - - swprintf_s(buf, TOAST_BUFSIZ, text, _T(UPDATES_RELEASE_INFO_URL), L"short", title, body); - __x_ABI_CWindows_CData_CXml_CDom_CIXmlDocument* inputXml = NULL; - String2IXMLDocument( - buf, - wcslen(buf), - &inputXml, - NULL - ); - if (*toast) - { - if (notifier) - { - notifier->lpVtbl->Hide(notifier, *toast); - } - (*toast)->lpVtbl->Release((*toast)); - (*toast) = NULL; - } - if (notifFactory) - { - notifFactory->lpVtbl->CreateToastNotification(notifFactory, inputXml, toast); - } - if ((*toast) && notifier) - { - notifier->lpVtbl->Show(notifier, *toast); - } - if (inputXml) - { - inputXml->lpVtbl->Release(inputXml); - } - if (hEPGui) - { - FreeLibrary(hEPGui); - } - SwitchToThread(); - - return TRUE; -} - -BOOL InstallUpdatesIfAvailable( - HMODULE hModule, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification** toast, - DWORD dwOperation, - DWORD bAllocConsole, - DWORD dwUpdatePolicy -) -{ - EP_L10N_ApplyPreferredLanguageForCurrentThread(); - HMODULE hEPGui = LoadGuiModule(); - - wchar_t wszInfoURL[MAX_PATH]; - ZeroMemory(wszInfoURL, MAX_PATH * sizeof(wchar_t)); - wcscat_s(wszInfoURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STABLE)); - wchar_t buf[TOAST_BUFSIZ]; - DWORD dwLeftMost = 0; - DWORD dwSecondLeft = 0; - DWORD dwSecondRight = 0; - DWORD dwRightMost = 0; - QueryVersionInfo(hModule, VS_VERSION_INFO, &dwLeftMost, &dwSecondLeft, &dwSecondRight, &dwRightMost); - - if (bAllocConsole) - { - switch (dwUpdatePolicy) - { - default: - case UPDATE_POLICY_AUTO: - { - dwUpdatePolicy = UPDATE_POLICY_AUTO; - printf("[Updates] Configured update policy on this system: \"Install updates automatically\".\n"); - break; - } - case UPDATE_POLICY_NOTIFY: - { - printf("[Updates] Configured update policy on this system: \"Check for updates but let me choose whether to download and install them\".\n"); - break; - } - case UPDATE_POLICY_MANUAL: - { - printf("[Updates] Configured update policy on this system: \"Manually check for updates\".\n"); - break; - } - } - } - - const wchar_t text[] = - L"\r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n" - L" \r\n"; - wchar_t title[100]; - wchar_t body[200]; - title[0] = 0; body[0] = 0; - - if (dwOperation == UPDATES_OP_INSTALL) - { - LoadStringW(hEPGui, IDS_UPDATES_DOWNLOADING_T, title, ARRAYSIZE(title)); - } - else if (dwOperation == UPDATES_OP_CHECK) - { - LoadStringW(hEPGui, IDS_UPDATES_CHECKING_T, title, ARRAYSIZE(title)); - } - - wchar_t bodyFormat[200]; - ZeroMemory(bodyFormat, sizeof(bodyFormat)); - if (LoadStringW(hEPGui, IDS_UPDATES_INSTALLEDVER, bodyFormat, ARRAYSIZE(bodyFormat))) - { - swprintf_s(body, ARRAYSIZE(body), bodyFormat, dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); - } - - swprintf_s(buf, TOAST_BUFSIZ, text, _T(UPDATES_RELEASE_INFO_URL), L"long", title, body); - - __x_ABI_CWindows_CData_CXml_CDom_CIXmlDocument* inputXml = NULL; - String2IXMLDocument( - buf, - wcslen(buf), - &inputXml, - NULL - ); - - if (dwOperation == UPDATES_OP_CHECK || dwOperation == UPDATES_OP_INSTALL) - { - if (*toast) - { - if (notifier) - { - notifier->lpVtbl->Hide(notifier, *toast); - } - (*toast)->lpVtbl->Release((*toast)); - (*toast) = NULL; - } - if (notifFactory) - { - notifFactory->lpVtbl->CreateToastNotification(notifFactory, inputXml, toast); - } - if ((*toast) && notifier) - { - notifier->lpVtbl->Show(notifier, *toast); - } - if (inputXml) - { - inputXml->lpVtbl->Release(inputXml); - } - } - - WCHAR dllName[MAX_PATH]; - GetModuleFileNameW(hModule, dllName, MAX_PATH); - wprintf(L"[Updates] Path to module: %s\n", dllName); - - CHAR hash[100]; - ZeroMemory(hash, 100 * sizeof(CHAR)); - GetHardcodedHash(dllName, hash, 100); - if (!strcmp(hash, "This")) ComputeFileHash2(hModule, dllName, hash, 100); - else printf("[Updates] Using hardcoded hash.\n"); - - BOOL bFail = FALSE, bReturnValue = FALSE; - dwLeftMost = 0; dwSecondLeft = 0; dwSecondRight = 0; dwRightMost = 0; - if (IsUpdateAvailable(_T(REGPATH), hash, &bFail, wszInfoURL, MAX_PATH, hModule, &dwLeftMost, &dwSecondLeft, &dwSecondRight, &dwRightMost)) - { - printf("[Updates] An update is available.\n"); - if ((dwOperation == UPDATES_OP_DEFAULT && dwUpdatePolicy == UPDATE_POLICY_AUTO) || (dwOperation == UPDATES_OP_INSTALL)) - { - __x_ABI_CWindows_CData_CXml_CDom_CIXmlDocument* inputXml = NULL; - BOOL bOk = UpdateProduct(_T(REGPATH), notifier, notifFactory, toast, hModule); - if (!bOk) - { - if (dwOperation == UPDATES_OP_INSTALL) - { - title[0] = 0; body[0] = 0; - LoadStringW(hEPGui, IDS_UPDATES_DLFAILED_T, title, ARRAYSIZE(title)); - LoadStringW(hEPGui, IDS_UPDATES_DLFAILED_B, body, ARRAYSIZE(body)); - - swprintf_s(buf, TOAST_BUFSIZ, text, _T(UPDATES_RELEASE_INFO_URL), L"short", title, body); - - String2IXMLDocument( - buf, - wcslen(buf), - &inputXml, - NULL - ); - } - } - if (bOk || (!bOk && (dwOperation == UPDATES_OP_INSTALL))) - { - if (*toast) - { - if (notifier) - { - notifier->lpVtbl->Hide(notifier, *toast); - } - (*toast)->lpVtbl->Release((*toast)); - (*toast) = NULL; - } - if (notifFactory) - { - notifFactory->lpVtbl->CreateToastNotification(notifFactory, inputXml, toast); - } - if ((*toast) && notifier) - { - notifier->lpVtbl->Show(notifier, *toast); - } - if (inputXml) - { - inputXml->lpVtbl->Release(inputXml); - } - } - } - else if ((dwOperation == UPDATES_OP_DEFAULT && dwUpdatePolicy == UPDATE_POLICY_NOTIFY) || (dwOperation == UPDATES_OP_CHECK)) - { - title[0] = 0; body[0] = 0; - - if (!dwLeftMost) - { - LoadStringW(hEPGui, IDS_UPDATES_AVAILABLE_T_U, title, ARRAYSIZE(title)); - } - else - { - WCHAR titleFormat[100]; - if (LoadStringW(hEPGui, IDS_UPDATES_AVAILABLE_T, titleFormat, ARRAYSIZE(titleFormat))) - { - swprintf_s(title, ARRAYSIZE(title), titleFormat, dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); - } - } - - LoadStringW(hEPGui, IDS_UPDATES_AVAILABLE_B, body, ARRAYSIZE(body)); - - swprintf_s(buf, TOAST_BUFSIZ, text, wszInfoURL, L"long", title, body); - - __x_ABI_CWindows_CData_CXml_CDom_CIXmlDocument* inputXml = NULL; - String2IXMLDocument( - buf, - wcslen(buf), - &inputXml, - NULL - ); - if (*toast) - { - if (notifier) - { - notifier->lpVtbl->Hide(notifier, *toast); - } - (*toast)->lpVtbl->Release((*toast)); - (*toast) = NULL; - } - if (notifFactory) - { - notifFactory->lpVtbl->CreateToastNotification(notifFactory, inputXml, toast); - } - if ((*toast) && notifier) - { - notifier->lpVtbl->Show(notifier, *toast); - } - if (inputXml) - { - inputXml->lpVtbl->Release(inputXml); - } - } - bReturnValue = TRUE; - } - else - { - if (bFail) - { - printf("[Updates] Unable to check for updates because the remote server is unavailable.\n"); - } - else - { - printf("[Updates] No updates are available.\n"); - } - if (dwOperation == UPDATES_OP_CHECK || dwOperation == UPDATES_OP_INSTALL) - { - title[0] = 0; body[0] = 0; - if (bFail) - { - LoadStringW(hEPGui, IDS_UPDATES_CHECKFAILED_T, title, ARRAYSIZE(title)); - LoadStringW(hEPGui, IDS_UPDATES_CHECKFAILED_B, body, ARRAYSIZE(body)); - } - else - { - LoadStringW(hEPGui, IDS_UPDATES_ISLATEST_T, title, ARRAYSIZE(title)); - LoadStringW(hEPGui, IDS_UPDATES_ISLATEST_B, body, ARRAYSIZE(body)); - } - - swprintf_s(buf, TOAST_BUFSIZ, text, _T(UPDATES_RELEASE_INFO_URL), L"short", title, body); - - __x_ABI_CWindows_CData_CXml_CDom_CIXmlDocument* inputXml = NULL; - String2IXMLDocument( - buf, - wcslen(buf), - &inputXml, - NULL - ); - if (*toast) - { - if (notifier) - { - notifier->lpVtbl->Hide(notifier, *toast); - } - (*toast)->lpVtbl->Release((*toast)); - (*toast) = NULL; - } - if (notifFactory) - { - notifFactory->lpVtbl->CreateToastNotification(notifFactory, inputXml, toast); - } - if ((*toast) && notifier) - { - notifier->lpVtbl->Show(notifier, *toast); - } - if (inputXml) - { - inputXml->lpVtbl->Release(inputXml); - } - } - bReturnValue = FALSE; - } - - if (hEPGui) - { - FreeLibrary(hEPGui); - } - - return bReturnValue; -} \ No newline at end of file diff --git a/ExplorerPatcher/updates.cpp b/ExplorerPatcher/updates.cpp new file mode 100644 index 0000000..4c10328 --- /dev/null +++ b/ExplorerPatcher/updates.cpp @@ -0,0 +1,1426 @@ +#include "updates.h" + +#include +#pragma comment(lib, "Shlwapi.lib") + +#include + +#include +#include +#include + +static HRESULT String2IXMLDocument(const wchar_t* pwszData, ABI::Windows::Data::Xml::Dom::IXmlDocument** pOutXmlToastMessage) +{ + using namespace Microsoft::WRL; + using namespace ABI::Windows::Data::Xml::Dom; + + *pOutXmlToastMessage = nullptr; + + CoInitialize(nullptr); + RoInitialize(RO_INIT_MULTITHREADED); + + ComPtr spInspectable; + RETURN_IF_FAILED(RoActivateInstance(Wrappers::HStringReference(RuntimeClass_Windows_Data_Xml_Dom_XmlDocument).Get(), &spInspectable)); + + ComPtr spXmlDocument; + RETURN_IF_FAILED(spInspectable.As(&spXmlDocument)); + spXmlDocument.CopyTo(pOutXmlToastMessage); + + ComPtr spXmlDocumentIO; + RETURN_IF_FAILED(spXmlDocument.As(&spXmlDocumentIO)); + + RETURN_IF_FAILED(spXmlDocumentIO->LoadXml(Wrappers::HStringReference(pwszData).Get())); + + return S_OK; +} + +using namespace Microsoft::WRL; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Data::Xml::Dom; +using namespace ABI::Windows::UI::Notifications; + +class CToastData : public RuntimeClass, ITypedEventHandler> +{ +public: + CToastData(IToastNotifier* notifier, IToastNotificationFactory* notifFactory) + : notifier(notifier), notifFactory(notifFactory), cookie{}, liLastUpdate{} + { + } + + void HideToast() + { + if (!this) return; + IToastNotification* oldToast = toast.Get(); + if (oldToast) + { + if (notifier) + { + notifier->Hide(oldToast); + } + oldToast->remove_Activated(cookie); + toast.Reset(); + } + } + + void HideAndShowToast(IXmlDocument* inputXml) + { + if (!this) return; + HideToast(); + if (notifFactory) + { + notifFactory->CreateToastNotification(inputXml, &toast); + } + IToastNotification* newToast = toast.Get(); + if (newToast && notifier) + { + ComPtr toast2; + if (SUCCEEDED(newToast->QueryInterface(IID_PPV_ARGS(&toast2)))) + { + toast2->put_Tag(Wrappers::HStringReference(L"ep_updates").Get()); + } + newToast->add_Activated(this, &cookie); + notifier->Show(newToast); + } + } + + HRESULT UpdateDownloadProgress(SIZE_T downloaded, SIZE_T total) + { + if (!this) return E_FAIL; + + LARGE_INTEGER liNow; + QueryPerformanceCounter(&liNow); + /*if (downloaded != 0 && liNow.QuadPart - liLastUpdate.QuadPart < 1000000) // 100ms + { + return S_FALSE; + }*/ + liLastUpdate = liNow; + + ComPtr dataInspectable; + RETURN_IF_FAILED(RoActivateInstance(Wrappers::HStringReference(RuntimeClass_Windows_UI_Notifications_NotificationData).Get(), &dataInspectable)); + + ComPtr data; + RETURN_IF_FAILED(dataInspectable.As(&data)); + + ComPtr> values; + RETURN_IF_FAILED(data->get_Values(&values)); + + WCHAR szProgressValue[64]; + if (total != 0) + { + swprintf_s(szProgressValue, L"%f", (double)downloaded / (double)total); + } + else + { + wcscpy_s(szProgressValue, ARRAYSIZE(szProgressValue), L"indeterminate"); + } + + WCHAR szProgressStatus[80]; + szProgressStatus[0] = 0; + + WCHAR szDownloaded[32]; + szDownloaded[0] = 0; + StrFormatByteSizeW(downloaded, szDownloaded, ARRAYSIZE(szDownloaded)); + + if (total != 0) + { + WCHAR szTotal[32]; + szTotal[0] = 0; + StrFormatByteSizeW(total, szTotal, ARRAYSIZE(szTotal)); + swprintf_s(szProgressStatus, L"%s / %s", szDownloaded, szTotal); + } + else if (downloaded != 0) + { + wcscpy_s(szProgressStatus, ARRAYSIZE(szProgressStatus), szDownloaded); + } + else + { + wil::unique_hmodule hEPGui(LoadGuiModule()); + LoadStringW(hEPGui.get(), IDS_UPDATES_DOWNLOADING_0, szProgressStatus, ARRAYSIZE(szProgressStatus)); + } + + BOOLEAN bReplaced; + RETURN_IF_FAILED(values->Insert( + Wrappers::HStringReference(L"progressValue").Get(), + Wrappers::HStringReference(szProgressValue, (UINT)wcslen(szProgressValue)).Get(), + &bReplaced + )); + RETURN_IF_FAILED(values->Insert( + Wrappers::HStringReference(L"progressStatus").Get(), + Wrappers::HStringReference(szProgressStatus, (UINT)wcslen(szProgressStatus)).Get(), + &bReplaced + )); + + ComPtr notifier2; + RETURN_IF_FAILED(notifier->QueryInterface(IID_PPV_ARGS(¬ifier2))); + + NotificationUpdateResult result; + RETURN_HR(notifier2->UpdateWithTag(data.Get(), Wrappers::HStringReference(L"ep_updates").Get(), &result)); + } + + STDMETHODIMP Invoke(IToastNotification* sender, IInspectable* argsInspectable) override + { + ComPtr args; + RETURN_IF_FAILED(argsInspectable->QueryInterface(IID_PPV_ARGS(&args))); + + Wrappers::HString arguments; + RETURN_IF_FAILED(args->get_Arguments(arguments.GetAddressOf())); + + const WCHAR* argumentsStr = arguments.GetRawBuffer(nullptr); + if (!wcscmp(argumentsStr, L"action=update")) + { + HANDLE hEvent = CreateEventW(nullptr, FALSE, FALSE, L"EP_Ev_InstallUpdatesNoConfirm_" _T(EP_CLSID)); + if (hEvent) + { + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + SetEvent(hEvent); + } + CloseHandle(hEvent); + } + } + + return S_OK; + } + +private: + IToastNotifier* notifier; + IToastNotificationFactory* notifFactory; + ComPtr toast; + EventRegistrationToken cookie; + LARGE_INTEGER liLastUpdate; +}; + +struct IsUpdateAvailableParameters +{ + HINTERNET hInternet; + HANDLE hEvent; +}; + +BOOL IsUpdateAvailableHelper( + WCHAR* url, + char* szCheckAgainst, + DWORD dwUpdateTimeout, + BOOL* lpFail, + CToastData* toastData, + BOOL bUpdatePreferStaging, + WCHAR* wszInfoURL, + DWORD cchInfoURL, + BOOL bNoConfirmation, + HMODULE hModule, + DWORD* pLeftMost, DWORD* pSecondLeft, DWORD* pSecondRight, DWORD* pRightMost) +{ + BOOL bIsUpdateAvailable = FALSE; + + IsUpdateAvailableParameters params; + params.hEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); + if (!params.hEvent) + { + return bIsUpdateAvailable; + } + + Wrappers::HString hstrDownloadUrl; + hstrDownloadUrl.Set(url); + HINTERNET hInternet = nullptr; + + if (bUpdatePreferStaging) + { + BOOL bRet = FALSE; + std::string jsonStr; + + hInternet = InternetOpenA(UPDATES_USER_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); + if ((bRet = (hInternet != nullptr))) + { + HINTERNET hConnect = InternetOpenUrlW( + hInternet, + hstrDownloadUrl.GetRawBuffer(nullptr), + nullptr, + 0, + INTERNET_FLAG_RAW_DATA | + INTERNET_FLAG_RELOAD | + INTERNET_FLAG_RESYNCHRONIZE | + INTERNET_FLAG_NO_COOKIES | + INTERNET_FLAG_NO_UI | + INTERNET_FLAG_NO_CACHE_WRITE | + INTERNET_FLAG_DONT_CACHE, + (DWORD_PTR)¶ms + ); + if ((bRet = (hConnect != nullptr))) + { + char* buffer = (char*)malloc(UPDATES_BUFSIZ); + if ((bRet = (buffer != nullptr))) + { + DWORD dwRead = 0; + while (InternetReadFile(hConnect, buffer, UPDATES_BUFSIZ, &dwRead) && dwRead != 0) + { + jsonStr.append(buffer, dwRead); + } + bRet = !jsonStr.empty(); + free(buffer); + } + InternetCloseHandle(hConnect); + } + InternetCloseHandle(hInternet); + } + + WCHAR* pszJsonStr = nullptr; + if (bRet) + { + pszJsonStr = (WCHAR*)malloc(sizeof(WCHAR) * (jsonStr.size() + 1)); + if ((bRet = (pszJsonStr != nullptr))) + { + MultiByteToWideChar( + CP_UTF8, + MB_PRECOMPOSED, + jsonStr.c_str(), + -1, + pszJsonStr, + jsonStr.size() + 1 + ); + } + } + + if (bRet) + { + auto extract = [&](HSTRING* outHtmlUrl, HSTRING* outDownloadUrl) -> HRESULT + { + using namespace ABI::Windows::Data::Json; + + *outHtmlUrl = nullptr; + *outDownloadUrl = nullptr; + + ComPtr jsonArrayStatics; + RETURN_IF_FAILED(GetActivationFactory(Wrappers::HStringReference(RuntimeClass_Windows_Data_Json_JsonArray).Get(), &jsonArrayStatics)); + + ComPtr releases; + RETURN_IF_FAILED(jsonArrayStatics->Parse(Wrappers::HStringReference(pszJsonStr).Get(), &releases)); + + ComPtr firstRelease; + RETURN_IF_FAILED(releases->GetObjectAt(0, &firstRelease)); + + RETURN_IF_FAILED(firstRelease->GetNamedString(Wrappers::HStringReference(L"html_url").Get(), outHtmlUrl)); + + ComPtr assets; + RETURN_IF_FAILED(firstRelease->GetNamedArray(Wrappers::HStringReference(L"assets").Get(), &assets)); + + ComPtr> assetsIterable; + RETURN_IF_FAILED(assets.As(&assetsIterable)); + + ComPtr> assetsIterator; + RETURN_IF_FAILED(assetsIterable->First(&assetsIterator)); + + boolean bHasCurrent = false; + RETURN_IF_FAILED(assetsIterator->get_HasCurrent(&bHasCurrent)); + + while (bHasCurrent) + { + ComPtr assetValue; + RETURN_IF_FAILED(assetsIterator->get_Current(&assetValue)); + + ComPtr asset; + RETURN_IF_FAILED(assetValue->GetObjectW(&asset)); // Note: W/A macros caused this to be renamed + + Wrappers::HString name; + RETURN_IF_FAILED(asset->GetNamedString(Wrappers::HStringReference(L"name").Get(), name.ReleaseAndGetAddressOf())); + + const WCHAR* pszName = name.GetRawBuffer(nullptr); + if (wcscmp(pszName, _T(SETUP_UTILITY_NAME)) == 0) + { + RETURN_IF_FAILED(asset->GetNamedString(Wrappers::HStringReference(L"browser_download_url").Get(), outDownloadUrl)); + return S_OK; + } + + RETURN_IF_FAILED(assetsIterator->MoveNext(&bHasCurrent)); + } + + RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); + }; + + Wrappers::HString htmlUrl; + if ((bRet = SUCCEEDED(extract(htmlUrl.ReleaseAndGetAddressOf(), hstrDownloadUrl.ReleaseAndGetAddressOf())))) + { + if (wszInfoURL) + { + wcscpy_s(wszInfoURL, cchInfoURL, htmlUrl.GetRawBuffer(nullptr)); + wprintf(L"[Updates] Release notes URL: \"%s\"\n", wszInfoURL); + } + if (hstrDownloadUrl.Get()) + { + wprintf(L"[Updates] Prerelease update URL: \"%s\"\n", hstrDownloadUrl.GetRawBuffer(nullptr)); + bUpdatePreferStaging = FALSE; // Success + } + } + } + + if (pszJsonStr) + { + free(pszJsonStr); + } + } + + if (!bUpdatePreferStaging && ((hInternet = InternetOpenA( + UPDATES_USER_AGENT, + INTERNET_OPEN_TYPE_PRECONFIG, + nullptr, + nullptr, + 0 + )))) + { + HINTERNET hConnect = InternetOpenUrlW( + hInternet, + hstrDownloadUrl.GetRawBuffer(nullptr), + nullptr, + 0, + INTERNET_FLAG_RAW_DATA | + INTERNET_FLAG_RELOAD | + INTERNET_FLAG_RESYNCHRONIZE | + INTERNET_FLAG_NO_COOKIES | + INTERNET_FLAG_NO_UI | + INTERNET_FLAG_NO_CACHE_WRITE | + INTERNET_FLAG_DONT_CACHE, + (DWORD_PTR)¶ms + ); + if (hConnect) + { + if (szCheckAgainst) + { + DWORD dwRead = 0; + char hash[DOSMODE_OFFSET + UPDATES_HASH_SIZE + 1] = {}; + if (InternetReadFile(hConnect, hash, DOSMODE_OFFSET + UPDATES_HASH_SIZE, &dwRead) + && dwRead == DOSMODE_OFFSET + UPDATES_HASH_SIZE) + { +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Hash of remote file is \"%s\" (%s).\n", DOSMODE_OFFSET + hash, (hash[0] == 0x4D && hash[1] == 0x5A) ? "valid" : "invalid"); +#endif + BOOL bOldType = TRUE; + char *szLeftMost = nullptr, *szSecondLeft = nullptr, *szSecondRight = nullptr, *szRightMost = nullptr, *szRealHash = nullptr; + if (hModule) + { + if (hash[0] == 0x4D && hash[1] == 0x5A) + { + if (strchr(DOSMODE_OFFSET + hash, '.')) + { + szLeftMost = DOSMODE_OFFSET + hash; + if ((szSecondLeft = strchr(szLeftMost, '.'))) + { + *szSecondLeft = 0; + szSecondLeft++; + if ((szSecondRight = strchr(szSecondLeft, '.'))) + { + *szSecondRight = 0; + szSecondRight++; + if ((szRightMost = strchr(szSecondRight, '.'))) + { + *szRightMost = 0; + szRightMost++; + if ((szRealHash = strchr(szRightMost, '.'))) + { + bOldType = FALSE; + + *szRealHash = 0; + szRealHash++; + DWORD dwRemoteLeftMost = atoi(szLeftMost); + if (pLeftMost) *pLeftMost = dwRemoteLeftMost - ((dwRemoteLeftMost == 22622 && szRealHash[0] != '!') ? 1 : 0); + DWORD dwRemoteSecondLeft = atoi(szSecondLeft); + if (pSecondLeft) *pSecondLeft = dwRemoteSecondLeft; + DWORD dwRemoteSecondRight = atoi(szSecondRight); + if (pSecondRight) *pSecondRight = dwRemoteSecondRight; + DWORD dwRemoteRightMost = atoi(szRightMost); + if (pRightMost) *pRightMost = dwRemoteRightMost; + DWORD dwLocalLeftMost = 0; + DWORD dwLocalSecondLeft = 0; + DWORD dwLocalSecondRight = 0; + DWORD dwLocalRightMost = 0; + BOOL bExtractedFromHash = FALSE; + CHAR hashCopy[100]; + strcpy_s(hashCopy, 100, szCheckAgainst); + char* szLocalLeftMost = nullptr, *szLocalSecondLeft = nullptr, *szLocalSecondRight = nullptr, *szLocalRightMost = nullptr, *szLocalRealHash = nullptr; + if (strchr(hashCopy, '.')) + { + szLocalLeftMost = hashCopy; + if ((szLocalSecondLeft = strchr(szLocalLeftMost, '.'))) + { + *szLocalSecondLeft = 0; + szLocalSecondLeft++; + if ((szLocalSecondRight = strchr(szLocalSecondLeft, '.'))) + { + *szLocalSecondRight = 0; + szLocalSecondRight++; + if ((szLocalRightMost = strchr(szLocalSecondRight, '.'))) + { + *szLocalRightMost = 0; + szLocalRightMost++; + if ((szLocalRealHash = strchr(szLocalRightMost, '.'))) + { + *szLocalRealHash = 0; + szLocalRealHash++; + + bExtractedFromHash = TRUE; + dwLocalLeftMost = atoi(szLocalLeftMost); + dwLocalSecondLeft = atoi(szLocalSecondLeft); + dwLocalSecondRight = atoi(szLocalSecondRight); + dwLocalRightMost = atoi(szLocalRightMost); +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Local version obtained from hash is %d.%d.%d.%d.\n", dwLocalLeftMost, dwLocalSecondLeft, dwLocalSecondRight, dwLocalRightMost); +#endif + } + } + } + } + } + if (!bExtractedFromHash) + { + QueryVersionInfo(hModule, VS_VERSION_INFO, &dwLocalLeftMost, &dwLocalSecondLeft, &dwLocalSecondRight, &dwLocalRightMost); + } + + int res = 0; + if (!res) + { + if (dwLocalLeftMost < dwRemoteLeftMost) + { + res = -1; + } + if (dwLocalLeftMost > dwRemoteLeftMost) + { + res = 1; + } + if (!res) + { + if (dwLocalSecondLeft < dwRemoteSecondLeft) + { + res = -1; + } + if (dwLocalSecondLeft > dwRemoteSecondLeft) + { + res = 1; + } + if (!res) + { + if (dwLocalSecondRight < dwRemoteSecondRight) + { + res = -1; + } + if (dwLocalSecondRight > dwRemoteSecondRight) + { + res = 1; + } + if (!res) + { + if (dwLocalRightMost < dwRemoteRightMost) + { + res = -1; + } + if (dwLocalRightMost > dwRemoteRightMost) + { + res = 1; + } + } + } + } + } + DWORD dwAllowDowngrades = FALSE, dwSize = sizeof(DWORD); + RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"UpdateAllowDowngrades", RRF_RT_DWORD, nullptr, &dwAllowDowngrades, &dwSize); + if ((res == 1 && dwAllowDowngrades) || res == -1) + { + bIsUpdateAvailable = TRUE; + } + else if (res == 0) + { + *(szSecondLeft - 1) = '.'; + *(szSecondRight - 1) = '.'; + *(szRightMost - 1) = '.'; + *(szRealHash - 1) = '.'; + if (_stricmp(DOSMODE_OFFSET + hash, szCheckAgainst)) + { + bIsUpdateAvailable = TRUE; + } + } + } + } + } + } + } + } + } + + /*if (bOldType) + { + if (hash[0] == 0x4D && hash[1] == 0x5A && _stricmp(DOSMODE_OFFSET + hash, szCheckAgainst)) + { + bIsUpdateAvailable = TRUE; + } + }*/ + } + else + { +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Failed. Read %d bytes.\n", dwRead); +#endif + if (lpFail) *lpFail = TRUE; + } + } + else + { + WCHAR wszPath[MAX_PATH] = {}; + SHGetFolderPathW(nullptr, SPECIAL_FOLDER_LEGACY, nullptr, SHGFP_TYPE_CURRENT, wszPath); + wcscat_s(wszPath, MAX_PATH, _T(APP_RELATIVE_PATH)); + BOOL bRet = CreateDirectoryW(wszPath, nullptr); + if (bRet || (!bRet && GetLastError() == ERROR_ALREADY_EXISTS)) + { + wcscat_s(wszPath, MAX_PATH, L"\\Update for " _T(PRODUCT_NAME) L" from "); + WCHAR wszURL[MAX_PATH] = {}; + wcscpy_s(wszURL, MAX_PATH, hstrDownloadUrl.GetRawBuffer(nullptr)); + if (wszURL[97]) // Truncate the URL for display + { + wszURL[96] = L'.'; + wszURL[97] = L'.'; + wszURL[98] = L'.'; + wszURL[99] = L'e'; + wszURL[100] = L'x'; + wszURL[101] = L'e'; + wszURL[102] = 0; + } + for (unsigned int i = 0; true; ++i) + { + if (!wszURL[i]) + { + break; + } + if (wszURL[i] == L'/') + { + wszURL[i] = L'\u2215'; + } + else if (wszURL[i] == L':') + { + wszURL[i] = L'\ua789'; + } + } + wcscat_s(wszPath, MAX_PATH, wszURL); +#ifdef UPDATES_VERBOSE_OUTPUT + wprintf(L"[Updates] Download path is \"%s\".\n", wszPath); +#endif + + BOOL bRet = DeleteFileW(wszPath); + if (bRet || (!bRet && GetLastError() == ERROR_FILE_NOT_FOUND)) + { + FILE* f = nullptr; + if (!_wfopen_s( + &f, + wszPath, + L"wb" + ) && f) + { + BYTE* buffer = (BYTE*)malloc(UPDATES_BUFSIZ); + if (buffer != nullptr) + { + DWORD totalSize = 0; + CHAR szContentLength[24] = {}; + DWORD dwSize = sizeof(szContentLength); + if (HttpQueryInfoA(hConnect, HTTP_QUERY_CONTENT_LENGTH, szContentLength, &dwSize, nullptr)) + { + totalSize = atoi(szContentLength); + } + toastData->UpdateDownloadProgress(0, totalSize); + + DWORD dwTotalRead = 0; + DWORD dwRead = 0; + bRet = FALSE; + while ((bRet = InternetReadFile( + hConnect, + buffer, + UPDATES_BUFSIZ, + &dwRead + ))) + { + if (dwRead == 0) + { + bIsUpdateAvailable = TRUE; +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Downloaded finished.\n"); +#endif + break; + } +#ifdef UPDATES_VERBOSE_OUTPUT + // printf("[Updates] Downloaded %d bytes.\n", dwRead); +#endif + fwrite( + buffer, + sizeof(BYTE), + dwRead, + f + ); + dwTotalRead += dwRead; + toastData->UpdateDownloadProgress(dwTotalRead, totalSize); + dwRead = 0; + } + free(buffer); + } + fclose(f); + } + if (bIsUpdateAvailable) + { + bIsUpdateAvailable = FALSE; +#ifdef UPDATES_VERBOSE_OUTPUT + printf( + "[Updates] In order to install this update for the product \"" + PRODUCT_NAME + "\", please allow the request.\n" + ); +#endif + + toastData->HideToast(); + + BOOL bHasErrored = FALSE; + BOOL bIsUACEnabled = FALSE; + DWORD(*CheckElevationEnabled)(BOOL*); + CheckElevationEnabled = (decltype(CheckElevationEnabled))GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CheckElevationEnabled"); + if (CheckElevationEnabled) CheckElevationEnabled(&bIsUACEnabled); + DWORD dwData = FALSE, dwSize = sizeof(DWORD); + RegGetValueW( + HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", + L"ConsentPromptBehaviorAdmin", + RRF_RT_DWORD, + nullptr, + &dwData, + &dwSize + ); + LPWSTR wszSID = nullptr; + HANDLE hMyToken = nullptr; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hMyToken)) + { + PTOKEN_USER ptu = nullptr; + DWORD dwSize = 0; + if (!GetTokenInformation(hMyToken, TokenUser, nullptr, 0, &dwSize) + && ERROR_INSUFFICIENT_BUFFER == GetLastError()) + { + if (nullptr != (ptu = (PTOKEN_USER)LocalAlloc(LPTR, dwSize))) + { + if (!GetTokenInformation(hMyToken, TokenUser, ptu, dwSize, &dwSize)) + { + LocalFree((HLOCAL)ptu); + return FALSE; + } + ConvertSidToStringSidW(ptu->User.Sid, &wszSID); + LocalFree((HLOCAL)ptu); + } + } + CloseHandle(hMyToken); + } + size_t lnSID = (wszSID ? wcslen(wszSID) : 0); + DWORD bIsBuiltInAdministratorInApprovalMode = FALSE; + BOOL bIsBuiltInAdministratorAccount = + IsUserAnAdmin() && + lnSID && !_wcsnicmp(wszSID, L"S-1-5-", 6) && wszSID[lnSID - 4] == L'-' && wszSID[lnSID - 3] == L'5' && wszSID[lnSID - 2] == L'0' && wszSID[lnSID - 1] == L'0'; + if (bIsBuiltInAdministratorAccount) + { + DWORD dwSSize = sizeof(DWORD); + RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", L"FilterAdministratorToken", RRF_RT_DWORD, nullptr, &bIsBuiltInAdministratorInApprovalMode, &dwSSize); + } + LocalFree(wszSID); + if (!bNoConfirmation && (!bIsUACEnabled || !dwData || (bIsBuiltInAdministratorAccount && !bIsBuiltInAdministratorInApprovalMode))) + { + WCHAR wszMsg[500]; + wszMsg[0] = 0; + + WCHAR wszMsgFormat[500]; + EP_L10N_ApplyPreferredLanguageForCurrentThread(); + { + wil::unique_hmodule hEPGui(LoadGuiModule()); + if (LoadStringW(hEPGui.get(), IDS_UPDATES_PROMPT, wszMsgFormat, ARRAYSIZE(wszMsgFormat))) + { + swprintf_s(wszMsg, ARRAYSIZE(wszMsg), wszMsgFormat, hstrDownloadUrl.GetRawBuffer(nullptr)); + } + } + + if (MessageBoxW( + FindWindowW(L"ExplorerPatcher_GUI_" _T(EP_CLSID), nullptr), + wszMsg, + _T(PRODUCT_NAME), + MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION + ) == IDNO) + { + bHasErrored = TRUE; + SetLastError(ERROR_CANCELLED); + } + } + + SHELLEXECUTEINFO ShExecInfo = { 0 }; + ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.hwnd = nullptr; + ShExecInfo.lpVerb = L"runas"; + ShExecInfo.lpFile = wszPath; + ShExecInfo.lpParameters = L"/update_silent"; + ShExecInfo.lpDirectory = nullptr; + ShExecInfo.nShow = SW_SHOW; + ShExecInfo.hInstApp = nullptr; + if (!bHasErrored && ShellExecuteExW(&ShExecInfo) && ShExecInfo.hProcess) + { + WaitForSingleObject(ShExecInfo.hProcess, INFINITE); + DWORD dwExitCode = 0; + if (GetExitCodeProcess(ShExecInfo.hProcess, &dwExitCode) && !dwExitCode) + { + bIsUpdateAvailable = TRUE; +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Update successful, File Explorer will probably restart momentarily.\n"); +#endif + } + else + { + SetLastError(dwExitCode); +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Update failed because the following error has occured: %d.\n", dwExitCode); +#endif + } + CloseHandle(ShExecInfo.hProcess); + } + else + { + DWORD dwError = GetLastError(); + if (dwError == ERROR_CANCELLED) + { +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Update failed because the request was denied.\n"); +#endif + } + else + { +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Update failed because the following error has occured: %d.\n", GetLastError()); +#endif + } + } + } + } + } + } + InternetCloseHandle(hConnect); + } + else + { + if (lpFail) *lpFail = TRUE; + } + InternetCloseHandle(hInternet); + } + + CloseHandle(params.hEvent); + + return bIsUpdateAvailable; +} + +BOOL IsUpdateAvailable(LPCWSTR wszDataStore, char* szCheckAgainst, BOOL* lpFail, WCHAR* wszInfoURL, DWORD cchInfoURL, HMODULE hModule, + DWORD* pLeftMost, DWORD* pSecondLeft, DWORD* pSecondRight, DWORD* pRightMost) +{ + HKEY hKey = nullptr; + DWORD dwSize = 0; + DWORD dwQueriedPolicy = 0; + BOOL bIsPolicyMatch = FALSE; + WCHAR szUpdateURL[MAX_PATH] = {}; + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STABLE)); +#ifdef UPDATES_VERBOSE_OUTPUT + printf("[Updates] Checking against hash \"%s\"\n", szCheckAgainst); +#endif + DWORD dwUpdateTimeout = UPDATES_DEFAULT_TIMEOUT; + DWORD bUpdatePreferStaging = FALSE; + + RegCreateKeyExW( + HKEY_CURRENT_USER, + wszDataStore, + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WOW64_64KEY, + nullptr, + &hKey, + nullptr + ); + if (hKey == nullptr || hKey == INVALID_HANDLE_VALUE) + { + hKey = nullptr; + } + if (hKey) + { + dwSize = sizeof(szUpdateURL); + RegQueryValueExW( + hKey, + L"UpdateURL", + nullptr, + nullptr, + (LPBYTE)szUpdateURL, + &dwSize + ); + if (dwSize == 1 && szUpdateURL[0] == 0) + { + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STABLE)); + } + wcscat_s(szUpdateURL, MAX_PATH, _T("/download/")); + wcscat_s(szUpdateURL, MAX_PATH, _T(SETUP_UTILITY_NAME)); + if (wszInfoURL) + { + dwSize = sizeof(WCHAR) * cchInfoURL; + RegQueryValueExW( + hKey, + L"UpdateURL", + nullptr, + nullptr, + (LPBYTE)wszInfoURL, + &dwSize + ); + if (dwSize == 1 && wszInfoURL[0] == 0) + { + wcscat_s(wszInfoURL, cchInfoURL, _T(UPDATES_RELEASE_INFO_URL_STABLE)); + } + } + dwSize = sizeof(DWORD); + RegQueryValueExW( + hKey, + L"UpdateTimeout", + nullptr, + nullptr, + (LPBYTE)&dwUpdateTimeout, + &dwSize + ); + dwSize = sizeof(DWORD); + RegQueryValueExW( + hKey, + L"UpdatePreferStaging", + nullptr, + nullptr, + (LPBYTE)&bUpdatePreferStaging, + &dwSize + ); + if (bUpdatePreferStaging) + { + szUpdateURL[0] = 0; + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STAGING)); + dwSize = sizeof(szUpdateURL); + RegQueryValueExW( + hKey, + L"UpdateURLStaging", + nullptr, + nullptr, + (LPBYTE)szUpdateURL, + &dwSize + ); + if (dwSize == 1 && szUpdateURL[0] == 0) + { + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STAGING)); + } + } + RegCloseKey(hKey); + } +#ifdef UPDATES_VERBOSE_OUTPUT + wprintf(L"[Updates] Update URL: %s\n", szUpdateURL); +#endif + return IsUpdateAvailableHelper( + szUpdateURL, + szCheckAgainst, + dwUpdateTimeout, + lpFail, + nullptr, + bUpdatePreferStaging, + wszInfoURL, + cchInfoURL, + FALSE, + hModule, + pLeftMost, pSecondLeft, pSecondRight, pRightMost + ); +} + +BOOL UpdateProduct(LPCWSTR wszDataStore, CToastData* toastData, BOOL bNoConfirmation, HMODULE hModule) +{ + HKEY hKey = nullptr; + DWORD dwSize = 0; + DWORD dwQueriedPolicy = 0; + BOOL bIsPolicyMatch = FALSE; + WCHAR szUpdateURL[MAX_PATH] = {}; + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STABLE)); + + DWORD dwUpdateTimeout = UPDATES_DEFAULT_TIMEOUT; + BOOL bUpdatePreferStaging = FALSE; + + RegCreateKeyExW( + HKEY_CURRENT_USER, + wszDataStore, + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WOW64_64KEY, + nullptr, + &hKey, + nullptr + ); + if (hKey == nullptr || hKey == INVALID_HANDLE_VALUE) + { + hKey = nullptr; + } + if (hKey) + { + dwSize = sizeof(szUpdateURL); + RegQueryValueExW( + hKey, + L"UpdateURL", + nullptr, + nullptr, + (LPBYTE)szUpdateURL, + &dwSize + ); + if (dwSize == 1 && szUpdateURL[0] == 0) + { + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STABLE)); + } + wcscat_s(szUpdateURL, MAX_PATH, _T("/download/")); + wcscat_s(szUpdateURL, MAX_PATH, _T(SETUP_UTILITY_NAME)); + dwSize = sizeof(DWORD); + RegQueryValueExW( + hKey, + L"UpdateTimeout", + nullptr, + nullptr, + (LPBYTE)&dwUpdateTimeout, + &dwSize + ); + dwSize = sizeof(BOOL); + RegQueryValueExW( + hKey, + L"UpdatePreferStaging", + nullptr, + nullptr, + (LPBYTE)&bUpdatePreferStaging, + &dwSize + ); + if (bUpdatePreferStaging) + { + szUpdateURL[0] = 0; + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STAGING)); + dwSize = sizeof(szUpdateURL); + RegQueryValueExW( + hKey, + L"UpdateURLStaging", + nullptr, + nullptr, + (LPBYTE)szUpdateURL, + &dwSize + ); + if (dwSize == 1 && szUpdateURL[0] == 0) + { + wcscat_s(szUpdateURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STAGING)); + } + } + RegCloseKey(hKey); + } +#ifdef UPDATES_VERBOSE_OUTPUT + wprintf(L"[Updates] Update URL: %s\n", szUpdateURL); +#endif + return IsUpdateAvailableHelper( + szUpdateURL, + nullptr, + dwUpdateTimeout, + nullptr, + toastData, + bUpdatePreferStaging, + nullptr, 0, + bNoConfirmation, + hModule, + nullptr, nullptr, nullptr, nullptr + ); +} + +BOOL ShowUpdateSuccessNotification(HMODULE hModule, CToastData* toastData) +{ + auto switchToThreadScopeExit = wil::scope_exit([](){ SwitchToThread(); }); + + EP_L10N_ApplyPreferredLanguageForCurrentThread(); + wil::unique_hmodule hEPGui(LoadGuiModule()); + + WCHAR buf[TOAST_BUFSIZ]; + DWORD dwLeftMost = 0; + DWORD dwSecondLeft = 0; + DWORD dwSecondRight = 0; + DWORD dwRightMost = 0; + QueryVersionInfo(hModule, VS_VERSION_INFO, &dwLeftMost, &dwSecondLeft, &dwSecondRight, &dwRightMost); + + const WCHAR text[] = + L"\r\n" + L" \r\n" + L" \r\n" + L" \r\n" + L" \r\n" + L" \r\n%s" // <-- Progress bar goes here + L" \r\n" + L" \r\n" + L" \r\n"; + WCHAR title[100]; + WCHAR body[200]; + title[0] = 0; body[0] = 0; + + LoadStringW(hEPGui.get(), IDS_UPDATES_SUCCESS_T, title, ARRAYSIZE(title)); + + WCHAR bodyFormat[200] = {}; + if (LoadStringW(hEPGui.get(), IDS_UPDATES_INSTALLEDVER, bodyFormat, ARRAYSIZE(bodyFormat))) + { + swprintf_s(body, ARRAYSIZE(body), bodyFormat, dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); + } + + swprintf_s(buf, TOAST_BUFSIZ, text, L"", L"short", title, body, L"", L""); + + ComPtr inputXml; + String2IXMLDocument(buf, &inputXml); + + toastData->HideAndShowToast(inputXml.Get()); + + return TRUE; +} + +BOOL InstallUpdatesIfAvailable( + HMODULE hModule, + CToastData* toastData, + DWORD dwOperation, + DWORD bAllocConsole, + DWORD dwUpdatePolicy) +{ + EP_L10N_ApplyPreferredLanguageForCurrentThread(); + wil::unique_hmodule hEPGui(LoadGuiModule()); + + WCHAR wszInfoURL[MAX_PATH] = {}; + wcscat_s(wszInfoURL, MAX_PATH, _T(UPDATES_RELEASE_INFO_URL_STABLE)); + WCHAR buf[TOAST_BUFSIZ]; + DWORD dwLeftMost = 0; + DWORD dwSecondLeft = 0; + DWORD dwSecondRight = 0; + DWORD dwRightMost = 0; + QueryVersionInfo(hModule, VS_VERSION_INFO, &dwLeftMost, &dwSecondLeft, &dwSecondRight, &dwRightMost); + + if (bAllocConsole) + { + switch (dwUpdatePolicy) + { + default: + case UPDATE_POLICY_AUTO: + { + dwUpdatePolicy = UPDATE_POLICY_AUTO; + printf("[Updates] Configured update policy on this system: \"Install updates automatically\".\n"); + break; + } + case UPDATE_POLICY_NOTIFY: + { + printf("[Updates] Configured update policy on this system: \"Check for updates but let me choose whether to download and install them\".\n"); + break; + } + case UPDATE_POLICY_MANUAL: + { + printf("[Updates] Configured update policy on this system: \"Manually check for updates\".\n"); + break; + } + } + } + + const WCHAR text[] = + L"\r\n" + L" \r\n" + L" \r\n" + L" \r\n" + L" \r\n" + L" \r\n%s" // <-- Progress bar goes here + L" \r\n" + L" \r\n" + L" \r\n"; + WCHAR title[100]; + WCHAR body[200]; + WCHAR actionsXml[200]; + WCHAR progressXml[200]; + title[0] = 0; body[0] = 0; actionsXml[0] = 0; progressXml[0] = 0; + + BOOL bIsInstall = dwOperation == UPDATES_OP_INSTALL || dwOperation == UPDATES_OP_INSTALL_NO_CONFIRM; + if (dwOperation == UPDATES_OP_CHECK || bIsInstall) + { + // title[0] = 0; body[0] = 0; actionsXml[0] = 0; progressXml[0] = 0; + + if (bIsInstall) + { + LoadStringW(hEPGui.get(), IDS_UPDATES_DOWNLOADING_T, title, ARRAYSIZE(title)); + + wcscpy_s(progressXml, ARRAYSIZE(progressXml), + L" \r\n" + ); + } + else if (dwOperation == UPDATES_OP_CHECK) + { + LoadStringW(hEPGui.get(), IDS_UPDATES_CHECKING_T, title, ARRAYSIZE(title)); + } + + WCHAR bodyFormat[200] = {}; + if (LoadStringW(hEPGui.get(), IDS_UPDATES_INSTALLEDVER, bodyFormat, ARRAYSIZE(bodyFormat))) + { + swprintf_s(body, ARRAYSIZE(body), bodyFormat, dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); + } + + swprintf_s(buf, TOAST_BUFSIZ, text, L"", L"long", title, body, progressXml, actionsXml); + + ComPtr inputXml; + String2IXMLDocument(buf, &inputXml); + + toastData->HideAndShowToast(inputXml.Get()); + + if (bIsInstall) + { + toastData->UpdateDownloadProgress(0, 0); + } + } + + WCHAR dllName[MAX_PATH]; + GetModuleFileNameW(hModule, dllName, MAX_PATH); + wprintf(L"[Updates] Path to module: %s\n", dllName); + + CHAR hash[100] = {}; + ComputeFileHash2(hModule, dllName, hash, 100); + + BOOL bFail = FALSE, bReturnValue = FALSE; + dwLeftMost = 0; dwSecondLeft = 0; dwSecondRight = 0; dwRightMost = 0; + if (IsUpdateAvailable(_T(REGPATH), hash, &bFail, wszInfoURL, MAX_PATH, hModule, &dwLeftMost, &dwSecondLeft, &dwSecondRight, &dwRightMost)) + { + printf("[Updates] An update is available.\n"); + if ((dwOperation == UPDATES_OP_DEFAULT && dwUpdatePolicy == UPDATE_POLICY_AUTO) || bIsInstall) + { + BOOL bOk = UpdateProduct(_T(REGPATH), toastData, dwOperation == UPDATES_OP_INSTALL_NO_CONFIRM, hModule); + if (!bOk && bIsInstall) + { + title[0] = 0; body[0] = 0; actionsXml[0] = 0; progressXml[0] = 0; + + LoadStringW(hEPGui.get(), IDS_UPDATES_DLFAILED_T, title, ARRAYSIZE(title)); + LoadStringW(hEPGui.get(), IDS_UPDATES_DLFAILED_B, body, ARRAYSIZE(body)); + + swprintf_s(buf, TOAST_BUFSIZ, text, L"", L"short", title, body, progressXml, actionsXml); + + ComPtr inputXml; + String2IXMLDocument(buf, &inputXml); + + toastData->HideAndShowToast(inputXml.Get()); + } + } + else if ((dwOperation == UPDATES_OP_DEFAULT && dwUpdatePolicy == UPDATE_POLICY_NOTIFY) || (dwOperation == UPDATES_OP_CHECK)) + { + title[0] = 0; body[0] = 0; actionsXml[0] = 0; progressXml[0] = 0; + + if (!dwLeftMost) + { + LoadStringW(hEPGui.get(), IDS_UPDATES_AVAILABLE_T_U, title, ARRAYSIZE(title)); + } + else + { + WCHAR titleFormat[100]; + if (LoadStringW(hEPGui.get(), IDS_UPDATES_AVAILABLE_T, titleFormat, ARRAYSIZE(titleFormat))) + { + swprintf_s(title, ARRAYSIZE(title), titleFormat, dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); + } + } + + LoadStringW(hEPGui.get(), IDS_UPDATES_AVAILABLE_B, body, ARRAYSIZE(body)); + + WCHAR action[100]; + action[0] = 0; + LoadStringW(hEPGui.get(), IDS_UPDATES_AVAILABLE_A, action, ARRAYSIZE(action)); + + swprintf_s(actionsXml, ARRAYSIZE(actionsXml), + L" \r\n" + L" \r\n" + L" \r\n", + action, L"action=update" + ); + + swprintf_s(buf, TOAST_BUFSIZ, text, wszInfoURL, L"long", title, body, progressXml, actionsXml); + + ComPtr inputXml; + String2IXMLDocument(buf, &inputXml); + + toastData->HideAndShowToast(inputXml.Get()); + } + + return TRUE; + } + else + { + if (bFail) + { + printf("[Updates] Unable to check for updates because the remote server is unavailable.\n"); + } + else + { + printf("[Updates] No updates are available.\n"); + } + if (dwOperation == UPDATES_OP_CHECK || bIsInstall) + { + title[0] = 0; body[0] = 0; actionsXml[0] = 0; progressXml[0] = 0; + + if (bFail) + { + LoadStringW(hEPGui.get(), IDS_UPDATES_CHECKFAILED_T, title, ARRAYSIZE(title)); + LoadStringW(hEPGui.get(), IDS_UPDATES_CHECKFAILED_B, body, ARRAYSIZE(body)); + } + else + { + LoadStringW(hEPGui.get(), IDS_UPDATES_ISLATEST_T, title, ARRAYSIZE(title)); + LoadStringW(hEPGui.get(), IDS_UPDATES_ISLATEST_B, body, ARRAYSIZE(body)); + } + + swprintf_s(buf, TOAST_BUFSIZ, text, L"", L"short", title, body, progressXml, actionsXml); + + ComPtr inputXml; + String2IXMLDocument(buf, &inputXml); + + toastData->HideAndShowToast(inputXml.Get()); + } + + return FALSE; + } +} + +extern "C" DWORD bAllocConsole; +extern "C" DWORD bShowUpdateToast; +extern "C" DWORD dwUpdatePolicy; + +DWORD CheckForUpdatesThread(LPVOID params) +{ + DWORD timeout = (DWORD)(UINT_PTR)params; + HRESULT hr = S_OK; + + if (SUCCEEDED(hr)) + { + hr = RoInitialize(RO_INIT_MULTITHREADED); + } + + ComPtr toastStatics; + if (SUCCEEDED(hr)) + { + hr = RoGetActivationFactory( + Wrappers::HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), + IID_PPV_ARGS(&toastStatics) + ); + } + + if (SUCCEEDED(hr)) + { + ComPtr toastStatics2; + if (SUCCEEDED(RoGetActivationFactory( + Wrappers::HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), + IID_PPV_ARGS(&toastStatics2) + ))) + { + ComPtr history; + if (SUCCEEDED(toastStatics2->get_History(&history))) + { + history->Remove(Wrappers::HStringReference(L"ep_updates").Get()); + } + } + } + + while (TRUE) + { + HWND hShell_TrayWnd = FindWindowExW(nullptr, nullptr, L"Shell_TrayWnd", nullptr); + if (hShell_TrayWnd) + { + Sleep(timeout); + break; + } + Sleep(100); + } + printf("[Updates] Starting daemon.\n"); + + ComPtr notifier; + if (SUCCEEDED(hr)) + { + hr = toastStatics->CreateToastNotifierWithId(Wrappers::HStringReference(APPID).Get(), ¬ifier); + } + + ComPtr notifFactory; + if (SUCCEEDED(hr)) + { + hr = RoGetActivationFactory( + Wrappers::HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), + IID_PPV_ARGS(¬ifFactory) + ); + } + + HANDLE hEvents[3]; + hEvents[0] = CreateEventW(nullptr, FALSE, FALSE, L"EP_Ev_CheckForUpdates_" _T(EP_CLSID)); + hEvents[1] = CreateEventW(nullptr, FALSE, FALSE, L"EP_Ev_InstallUpdates_" _T(EP_CLSID)); + hEvents[2] = CreateEventW(nullptr, FALSE, FALSE, L"EP_Ev_InstallUpdatesNoConfirm_" _T(EP_CLSID)); + if (hEvents[0] && hEvents[1] && hEvents[2]) + { + ComPtr toastData = Make(notifier.Get(), notifFactory.Get()); + if (bShowUpdateToast) + { + ShowUpdateSuccessNotification(hModule, toastData.Get()); + + HKEY hKey = nullptr; + + RegCreateKeyExW( + HKEY_CURRENT_USER, + TEXT(REGPATH), + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WOW64_64KEY | KEY_WRITE, + nullptr, + &hKey, + nullptr + ); + if (hKey == nullptr || hKey == INVALID_HANDLE_VALUE) + { + hKey = nullptr; + } + if (hKey) + { + bShowUpdateToast = FALSE; + RegSetValueExW( + hKey, + TEXT("IsUpdatePending"), + 0, + REG_DWORD, + (const BYTE*)&bShowUpdateToast, + sizeof(DWORD) + ); + RegCloseKey(hKey); + } + } + if (dwUpdatePolicy != UPDATE_POLICY_MANUAL) + { + InstallUpdatesIfAvailable(hModule, toastData.Get(), UPDATES_OP_DEFAULT, bAllocConsole, dwUpdatePolicy); + } + while (TRUE) + { + switch (WaitForMultipleObjects(3, hEvents, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + { + InstallUpdatesIfAvailable(hModule, toastData.Get(), UPDATES_OP_CHECK, bAllocConsole, dwUpdatePolicy); + break; + } + case WAIT_OBJECT_0 + 1: + { + InstallUpdatesIfAvailable(hModule, toastData.Get(), UPDATES_OP_INSTALL, bAllocConsole, dwUpdatePolicy); + break; + } + case WAIT_OBJECT_0 + 2: + { + InstallUpdatesIfAvailable(hModule, toastData.Get(), UPDATES_OP_INSTALL_NO_CONFIRM, bAllocConsole, dwUpdatePolicy); + break; + } + default: + { + break; + } + } + } + + // Unreachable, but just in case + CloseHandle(hEvents[0]); + CloseHandle(hEvents[1]); + CloseHandle(hEvents[2]); + } + + return 0; +} diff --git a/ExplorerPatcher/updates.h b/ExplorerPatcher/updates.h index 40509f9..f2f1c68 100644 --- a/ExplorerPatcher/updates.h +++ b/ExplorerPatcher/updates.h @@ -8,6 +8,10 @@ #include "utility.h" #include "../ep_gui/resources/EPSharedResources.h" +#ifdef __cplusplus +extern "C" { +#endif + extern HMODULE hModule; #define UPDATES_VERBOSE_OUTPUT @@ -20,6 +24,7 @@ extern HMODULE hModule; #define UPDATES_OP_DEFAULT 0 #define UPDATES_OP_CHECK 1 #define UPDATES_OP_INSTALL 2 +#define UPDATES_OP_INSTALL_NO_CONFIRM 3 #define UPDATES_USER_AGENT "ExplorerPatcher" #define UPDATES_FORM_HEADERS "Content-Type: text/plain;\r\n" @@ -31,24 +36,10 @@ extern HMODULE hModule; #define UPDATES_RELEASE_INFO_URL_STABLE "https://github.com/valinet/ExplorerPatcher/releases/latest" #define UPDATES_RELEASE_INFO_URL_STAGING "https://api.github.com/repos/valinet/ExplorerPatcher/releases?per_page=1" -typedef struct IsUpdateAvailableParameters -{ - HINTERNET hInternet; - HANDLE hEvent; -}; - -BOOL IsUpdatePolicy(LPCWSTR wszDataStore, DWORD dwUpdatePolicy); -BOOL ShowUpdateSuccessNotification( - HMODULE hModule, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification** toast -); -BOOL InstallUpdatesIfAvailable( - HMODULE hModule, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotifier* notifier, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotificationFactory* notifFactory, - __x_ABI_CWindows_CUI_CNotifications_CIToastNotification** toast, - DWORD dwOperation, DWORD bAllocConsole, DWORD dwUpdatePolicy -); -#endif \ No newline at end of file +DWORD CheckForUpdatesThread(LPVOID params); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ep_gui/resources/EPSharedResources.h b/ep_gui/resources/EPSharedResources.h index d2dd436..c975841 100644 --- a/ep_gui/resources/EPSharedResources.h +++ b/ep_gui/resources/EPSharedResources.h @@ -16,6 +16,8 @@ #define IDS_UPDATES_DLFAILED_B 312 #define IDS_UPDATES_INSTALLEDVER 313 #define IDS_UPDATES_PROMPT 314 +#define IDS_UPDATES_AVAILABLE_A 315 +#define IDS_UPDATES_DOWNLOADING_0 316 // 351-400 diff --git a/ep_gui/resources/lang/ep_gui.en-US.rc b/ep_gui/resources/lang/ep_gui.en-US.rc index 89c0a74..3e545f9 100644 --- a/ep_gui/resources/lang/ep_gui.en-US.rc +++ b/ep_gui/resources/lang/ep_gui.en-US.rc @@ -8,12 +8,14 @@ BEGIN IDS_UPDATES_CHECKING_T "Checking for updates…" IDS_UPDATES_AVAILABLE_T "Version %d.%d.%d.%d is available" IDS_UPDATES_AVAILABLE_T_U "New version available" - IDS_UPDATES_AVAILABLE_B "You can update by right clicking the taskbar, choosing ""Properties"", then ""Updates"". Click here to learn more about this update." + IDS_UPDATES_AVAILABLE_B "Click here to learn more about this update." + IDS_UPDATES_AVAILABLE_A "Update now" IDS_UPDATES_ISLATEST_T "No updates are available" IDS_UPDATES_ISLATEST_B "Please check back later." IDS_UPDATES_CHECKFAILED_T "Unable to check for updates" IDS_UPDATES_CHECKFAILED_B "Make sure that you are connected to the Internet and that the remote server is online." IDS_UPDATES_DOWNLOADING_T "Downloading and installing updates…" + IDS_UPDATES_DOWNLOADING_0 "Preparing…" IDS_UPDATES_SUCCESS_T "Update successful" IDS_UPDATES_DLFAILED_T "Update failed" IDS_UPDATES_DLFAILED_B "The has cancelled the process or an error has occured when attempting to install this update."