mirror of
https://github.com/valinet/ExplorerPatcher.git
synced 2025-01-26 16:13:43 +01:00
All: Implemented a mechanism to stop repeated crashes
When Windows is updated, it may fundamentally change the layout and functionality of `explorer`'s internal data structure. Since ExplorerPatcher intensively patches those in order to deliver its functionality, it may lead to `explorer` crashing when it performs these modifications while being unaware (outdated) of the latest OS changes. The worst scenario is when the crashes happen when `explorer` starts up (for example, when the user logs in), and could cause the user to experience an endless loop of `explorer` crashes, and leave him/her unable to practically use the computer. In order to mitigate this scenario, when `explorer` starts up as the shell, ExplorerPatcher increments a counter stored in the registry. After a predefined timeout (by default, 10 seconds), ExplorerPatcher will reset the counter to 0. If `explorer` crashes during this period, the counter will not be reset. When `explorer` restarts, the cycle repeats. If the counter reaches a predefined value (by default, 3), instead of starting up again normally (and very probably crashing again), ExplorerPatcher will display a message window informing the user about what is happening, offering a few suggestions on how to proceed next and disable its entire functionality until the next File Explorer restart, in order to give the user a chance to perform maintenance on the machine.
This commit is contained in:
parent
0ad140c47f
commit
d7e5b7d3c9
@ -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 <A HREF=\"eplink://update\">update ExplorerPatcher and restart File Explorer</A>.\n"
|
||||
L"\u2022 On GitHub, you can <A HREF=\"https://github.com/valinet/ExplorerPatcher/releases\">view releases</A>, <A HREF=\"https://github.com/valinet/ExplorerPatcher/discussions/1102\">check the current status</A>, <A HREF=\"https://github.com/valinet/ExplorerPatcher/discussions\">discuss</A> or <A HREF=\"https://github.com/valinet/ExplorerPatcher/issues\">review the latest issues</A>.\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 <A HREF=\"%s\">uninstall ExplorerPatcher</A>, 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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user