diff --git a/ExplorerPatcher/dllmain.c b/ExplorerPatcher/dllmain.c index d94bf71..b69a9fb 100644 --- a/ExplorerPatcher/dllmain.c +++ b/ExplorerPatcher/dllmain.c @@ -228,7 +228,7 @@ HRESULT WINAPI _DllGetClassObject( #pragma region "Updates" #ifdef _WIN64 -DWORD CheckForUpdatesThread(LPVOID unused) +DWORD CheckForUpdatesThread(LPVOID timeout) { HRESULT hr = S_OK; HSTRING_HEADER header_AppIdHString; @@ -252,7 +252,7 @@ DWORD CheckForUpdatesThread(LPVOID unused) ); if (hShell_TrayWnd) { - Sleep(5000); + Sleep(timeout); break; } Sleep(100); @@ -10783,7 +10783,7 @@ DWORD Inject(BOOL bIsExplorer) - CreateThread(NULL, 0, CheckForUpdatesThread, 0, 0, NULL); + CreateThread(NULL, 0, CheckForUpdatesThread, 5000, 0, NULL); @@ -12249,6 +12249,90 @@ void InjectShellExperienceHostFor22H2OrHigher() { #endif } +HRESULT InformUserAboutCrashCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) { + if (msg == TDN_HYPERLINK_CLICKED) { + if (wcschr(lParam, L'\'')) { + for (int i = 0; i < wcslen(lParam); ++i) if (*(((wchar_t*)lParam) + i) == L'\'') *(((wchar_t*)lParam) + i) = L'"'; + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + if (CreateProcessW(NULL, lParam, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + for (int i = 0; i < wcslen(lParam); ++i) if (*(((wchar_t*)lParam) + i) == L'"') *(((wchar_t*)lParam) + i) = L'\''; + } + else if (!wcsncmp(lParam, L"eplink://update", 15)) { + if (!wcsncmp(lParam, L"eplink://update/stable", 22)) { + DWORD no = 0; + RegSetKeyValueW(HKEY_CURRENT_USER, _T(REGPATH), L"UpdatePreferStaging", REG_DWORD, &no, sizeof(DWORD)); + } else if (!wcsncmp(lParam, L"eplink://update/staging", 23)) { + DWORD yes = 1; + RegSetKeyValueW(HKEY_CURRENT_USER, _T(REGPATH), L"UpdatePreferStaging", REG_DWORD, &yes, sizeof(DWORD)); + } + SetLastError(0); + HANDLE sigInstallUpdates = CreateEventW(NULL, FALSE, FALSE, L"EP_Ev_InstallUpdates_" _T(EP_CLSID)); + if (sigInstallUpdates && GetLastError() == ERROR_ALREADY_EXISTS) { + SetEvent(sigInstallUpdates); + } + else { + CreateThread(NULL, 0, CheckForUpdatesThread, 0, 0, NULL); + Sleep(100); + SetLastError(0); + sigInstallUpdates = CreateEventW(NULL, FALSE, FALSE, L"EP_Ev_InstallUpdates_" _T(EP_CLSID)); + if (sigInstallUpdates && GetLastError() == ERROR_ALREADY_EXISTS) { + SetEvent(sigInstallUpdates); + } + } + } + else ShellExecuteW(NULL, L"open", lParam, L"", NULL, SW_SHOWNORMAL); + return S_FALSE; + } + return S_OK; +} + +DWORD InformUserAboutCrash(LPVOID msg) { + TASKDIALOG_BUTTON buttons[1]; + buttons[0].nButtonID = IDNO; + buttons[0].pszButtonText = L"Dismiss"; + + TASKDIALOGCONFIG td; + ZeroMemory(&td, sizeof(TASKDIALOGCONFIG)); + td.cbSize = sizeof(TASKDIALOGCONFIG); + td.hInstance = hModule; + td.hwndParent = NULL; + td.dwFlags = TDF_SIZE_TO_CONTENT | TDF_ENABLE_HYPERLINKS; + td.pszWindowTitle = L"ExplorerPatcher"; + td.pszMainInstruction = L"Unfortunately, File Explorer is crashing :("; + td.pszContent = msg; + td.cButtons = sizeof buttons / sizeof buttons[0]; + td.pButtons = buttons; + td.cRadioButtons = 0; + td.pRadioButtons = NULL; + td.cxWidth = 0; + td.pfCallback = InformUserAboutCrashCallback; + + HMODULE hComCtl32 = NULL; + HRESULT(*pfTaskDialogIndirect)(const TASKDIALOGCONFIG*, int*, int*, BOOL*) = NULL; + int res = td.nDefaultButton; + if (!(hComCtl32 = GetModuleHandleA("Comctl32.dll")) || + !(pfTaskDialogIndirect = GetProcAddress(hComCtl32, "TaskDialogIndirect")) || + FAILED(pfTaskDialogIndirect(&td, &res, NULL, NULL))) { + wcscat_s(msg, 10000, L" Would you like to open the ExplorerPatcher status web page on GitHub in your default browser?"); + res = MessageBoxW(NULL, msg, L"ExplorerPatcher", MB_ICONASTERISK | MB_YESNO); + } + if (res == IDYES) ShellExecuteW(NULL, L"open", L"https://github.com/valinet/ExplorerPatcher/discussions/1102", L"", NULL, SW_SHOWNORMAL); + free(msg); +} + +DWORD WINAPI ClearCrashCounter(INT64 timeout) { + Sleep(timeout); + DWORD zero = 0; + RegSetKeyValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashCounter", REG_DWORD, &zero, sizeof(DWORD)); +} + #define DLL_INJECTION_METHOD_DXGI 0 #define DLL_INJECTION_METHOD_COM 1 #define DLL_INJECTION_METHOD_START_INJECTION 2 @@ -12336,7 +12420,55 @@ HRESULT EntryPoint(DWORD dwMethod) bIsExplorerProcess = bIsThisExplorer; if (bIsThisExplorer) { - Inject(!IsDesktopWindowAlreadyPresent()); + BOOL desktopExists = IsDesktopWindowAlreadyPresent(); + if (!desktopExists) { + DWORD crashCounterDisabled = 0, crashCounter = 0, crashThresholdTime = 10000, crashCounterThreshold = 3, dwTCSize = sizeof(DWORD); + RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashCounterDisabled", RRF_RT_DWORD, NULL, &crashCounterDisabled, &dwTCSize); dwTCSize = sizeof(DWORD); + if (crashCounterDisabled != 0 && crashCounterDisabled != 1) crashCounterDisabled = 0; + RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashCounter", RRF_RT_DWORD, NULL, &crashCounter, &dwTCSize); dwTCSize = sizeof(DWORD); + if (crashCounter < 0) crashCounter = 0; + RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashThresholdTime", RRF_RT_DWORD, NULL, &crashThresholdTime, &dwTCSize); dwTCSize = sizeof(DWORD); + if (crashThresholdTime < 100 || crashThresholdTime > 60000) crashThresholdTime = 10000; + RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashCounterThreshold", RRF_RT_DWORD, NULL, &crashCounterThreshold, &dwTCSize); dwTCSize = sizeof(DWORD); + if (crashCounterThreshold <= 1 || crashCounterThreshold >= 10) crashCounterThreshold = 3; + if (!crashCounterDisabled) { + if (crashCounter >= crashCounterThreshold) { + crashCounter = 0; + RegSetKeyValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashCounter", REG_DWORD, &crashCounter, sizeof(DWORD)); + wchar_t times[100]; + ZeroMemory(times, sizeof(wchar_t) * 100); + swprintf_s(times, 100, crashCounterThreshold == 1 ? L"once" : L"%d times", crashCounterThreshold); + wchar_t uninstallLink[MAX_PATH]; + ZeroMemory(uninstallLink, sizeof(wchar_t) * MAX_PATH); + uninstallLink[0] = L'\''; + SHGetFolderPathW(NULL, SPECIAL_FOLDER, NULL, SHGFP_TYPE_CURRENT, uninstallLink + 1); + wcscat_s(uninstallLink, MAX_PATH, _T(APP_RELATIVE_PATH) L"\\" _T(SETUP_UTILITY_NAME) L"' /uninstall"); + wchar_t* msg = calloc(sizeof(wchar_t), 10000); + swprintf_s(msg, 10000, + L"It seems that File Explorer closed unexpectedly %s in less than %d seconds each time when starting up. " + L"This might indicate a problem caused by ExplorerPatcher, which might be unaware of recent changes in Windows, for example " + L"when running on a new OS build.\n" + L"Here are a few recommendations:\n" + L"\u2022 If an updated version is available, you can update ExplorerPatcher and restart File Explorer.\n" + L"\u2022 On GitHub, you can view releases, check the current status, discuss or review the latest issues.\n" + L"\u2022 If you suspect this is not caused by ExplorerPatcher, please uninstall any recently installed shell extensions or similar utilities.\n" + L"\u2022 If no fix is available for the time being, you can uninstall ExplorerPatcher, and then later reinstall it when a fix is published on " + L"GitHub. Rest assured, even if you uninstall, your program configuration will be preserved.\n" + L"\n" + L"I am sorry for the inconvenience this might cause; I am doing my best to try to keep this program updated and working.\n\n" + L"ExplorerPatcher is disabled until the next File Explorer restart, in order to allow you to perform maintenance tasks and take the necessary actions.", + times, crashThresholdTime / 1000, uninstallLink); + SHCreateThread(InformUserAboutCrash, msg, 0, NULL); + IncrementDLLReferenceCount(hModule); + bInstanced = TRUE; + return E_NOINTERFACE; + } + crashCounter++; + RegSetKeyValueW(HKEY_CURRENT_USER, _T(REGPATH), L"CrashCounter", REG_DWORD, &crashCounter, sizeof(DWORD)); + SHCreateThread(ClearCrashCounter, crashThresholdTime, 0, NULL); + } + } + Inject(!desktopExists); IncrementDLLReferenceCount(hModule); bInstanced = TRUE; }