Index: phlib/include/ph.h =================================================================== --- phlib/include/ph.h (revision 3954) +++ phlib/include/ph.h (working copy) @@ -2078,6 +2078,22 @@ #define PhShowWarning(hWnd, Format, ...) PhShowMessage(hWnd, MB_OK | MB_ICONWARNING, Format, __VA_ARGS__) #define PhShowInformation(hWnd, Format, ...) PhShowMessage(hWnd, MB_OK | MB_ICONINFORMATION, Format, __VA_ARGS__) +#define PH_USER_STATUS_QUERY_CONTINUE 0x1 + +typedef BOOLEAN (NTAPI *PPH_USER_STATUS_MESSAGE_CALLBACK)( + __in HWND hWnd, + __in_opt PWSTR Message, + __in NTSTATUS Status, + __in_opt ULONG Win32Result, + __in_opt PPH_STRING SystemMessage, + __in ULONG Type, + __out_opt PULONG_PTR Result + ); + +PPH_USER_STATUS_MESSAGE_CALLBACK PhSetUserStatusMessageCallback( + __in PPH_USER_STATUS_MESSAGE_CALLBACK Callback + ); + PHLIBAPI VOID PhShowStatus( __in HWND hWnd, Index: phlib/support.c =================================================================== --- phlib/support.c (revision 3954) +++ phlib/support.c (working copy) @@ -52,6 +52,8 @@ DECLSPEC_SELECTANY WCHAR *PhSizeUnitNames[7] = { L"B", L"kB", L"MB", L"GB", L"TB", L"PB", L"EB" }; DECLSPEC_SELECTANY ULONG PhMaxSizeUnit = MAXULONG32; +static PPH_USER_STATUS_MESSAGE_CALLBACK PhUserStatusMessageCallback = NULL; + /** * Ensures a rectangle is positioned within the * specified bounds. @@ -400,6 +402,16 @@ return PhGetWin32Message(Win32Result); } +PPH_USER_STATUS_MESSAGE_CALLBACK PhSetUserStatusMessageCallback( + __in PPH_USER_STATUS_MESSAGE_CALLBACK Callback + ) +{ + return _InterlockedExchangePointer( + (PPVOID)&PhUserStatusMessageCallback, + Callback + ); +} + /** * Displays an error message for a NTSTATUS value or Win32 error code. * @@ -416,9 +428,30 @@ ) { PPH_STRING statusMessage; + PPH_USER_STATUS_MESSAGE_CALLBACK callback; statusMessage = PhpGetStatusMessage(Status, Win32Result); + callback = PhUserStatusMessageCallback; + if (callback) + { + if (callback( + hWnd, + Message, + Status, + Win32Result, + statusMessage, + 0, + NULL + )) + { + if (statusMessage) + PhDereferenceObject(statusMessage); + + return; + } + } + if (!statusMessage) { if (Message) @@ -465,10 +498,33 @@ ) { PPH_STRING statusMessage; + PPH_USER_STATUS_MESSAGE_CALLBACK callback; INT result; statusMessage = PhpGetStatusMessage(Status, Win32Result); + callback = PhUserStatusMessageCallback; + if (callback) + { + ULONG_PTR callbackResult = 0; + + if (callback( + hWnd, + Message, + Status, + Win32Result, + statusMessage, + PH_USER_STATUS_QUERY_CONTINUE, + &callbackResult + )) + { + if (statusMessage) + PhDereferenceObject(statusMessage); + + return !!callbackResult; + } + } + if (!statusMessage) { if (Message) Index: ProcessHacker/actions.c =================================================================== --- ProcessHacker/actions.c (revision 3954) +++ ProcessHacker/actions.c (working copy) @@ -1046,7 +1046,7 @@ if (!DebuggerCommand) { - PhShowError(hWnd, L"Unable to locate the debugger."); + PhShowError(hWnd, L"Unable to locate the debugger. Make sure you have installed and configured a debugger correctly."); return FALSE; } Index: ProcessHacker/appsup.c =================================================================== --- ProcessHacker/appsup.c (revision 3954) +++ ProcessHacker/appsup.c (working copy) @@ -463,6 +463,147 @@ } } +PWSTR PhpGetFriendlyErrorMessage( + __in NTSTATUS Status, + __in_opt ULONG Win32Result + ) +{ + if (!Win32Result) + Win32Result = PhNtStatusToDosError(Status); + + switch (Win32Result) + { + case ERROR_ACCESS_DENIED: + if (PhElevated) + return L"Permission was denied."; + else + return L"Permission was denied. Run Process Hacker with administrative privileges and try again."; + } + + switch (Status) + { + case STATUS_INVALID_CID: + return L"The process or thread does not exist. It may have terminated already."; + case STATUS_HANDLE_NOT_CLOSABLE: + if (PhKphHandle) + return L"The handle is protected. Remove the Protected attribute of the handle and try again."; + else + return L"The handle is protected."; + } + + return NULL; +} + +BOOLEAN PhUiUserStatusMessageCallback( + __in HWND hWnd, + __in_opt PWSTR Message, + __in NTSTATUS Status, + __in_opt ULONG Win32Result, + __in_opt PPH_STRING SystemMessage, + __in ULONG Type, + __out_opt PULONG_PTR Result + ) +{ + BOOLEAN result = TRUE; + PH_AUTO_POOL autoPool; + TASKDIALOGCONFIG config = { sizeof(config) }; + TASKDIALOG_BUTTON buttons[1]; + INT button; + + if (!PhGetIntegerSetting(L"EnableFriendly")) + return FALSE; + if (!TaskDialogIndirect_I) + return FALSE; + + PhInitializeAutoPool(&autoPool); + + config.hwndParent = hWnd; + config.hInstance = PhInstanceHandle; + config.pszWindowTitle = L"Process Hacker"; + config.pszMainIcon = TD_ERROR_ICON; + + if (Message) + { + config.pszMainInstruction = PhaConcatStrings2(Message, L".")->Buffer; + } + else + { + config.pszMainInstruction = L"Unable to perform the operation."; + } + + config.pszCollapsedControlText = L"Show details"; + config.pszExpandedControlText = L"Hide details"; + + config.pszContent = PhpGetFriendlyErrorMessage(Status, Win32Result); + + if (config.pszContent) + { + // We put the friendly message in the content area, so put the details + // in the expanded information area. + + if (Win32Result) + config.pszExpandedInformation = PhaFormatString(L"Win32 error %u: %s", Win32Result, PhGetStringOrDefault(SystemMessage, L"Unknown"))->Buffer; + else + config.pszExpandedInformation = PhaFormatString(L"Status 0x%x: %s", Status, PhGetStringOrDefault(SystemMessage, L"Unknown"))->Buffer; + } + else + { + // We don't have a friendly message, so put the deails in the content area. + + config.pszContent = PhGetStringOrDefault(SystemMessage, L"An unknown error occurred."); + + if (Win32Result) + config.pszExpandedInformation = PhaFormatString(L"Win32 error %u.", Win32Result)->Buffer; + else + config.pszExpandedInformation = PhaFormatString(L"Status 0x%x.", Status)->Buffer; + } + + if (Type == 0) + { + config.dwCommonButtons = TDCBF_CLOSE_BUTTON; + config.nDefaultButton = TDCBF_CLOSE_BUTTON; + + if (TaskDialogIndirect_I( + &config, + NULL, + NULL, + NULL + ) != S_OK) + { + result = FALSE; + } + } + else if (Type == PH_USER_STATUS_QUERY_CONTINUE) + { + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + + buttons[0].nButtonID = IDYES; + buttons[0].pszButtonText = L"Continue"; + + config.cButtons = 1; + config.pButtons = buttons; + config.nDefaultButton = IDYES; + + if (TaskDialogIndirect_I( + &config, + &button, + NULL, + NULL + ) == S_OK) + { + *Result = button == IDYES; + } + else + { + result = FALSE; + } + } + + PhDeleteAutoPool(&autoPool); + + return result; +} + VOID PhSearchOnlineString( __in HWND hWnd, __in PWSTR String Index: ProcessHacker/include/phapp.h =================================================================== --- ProcessHacker/include/phapp.h (revision 3954) +++ ProcessHacker/include/phapp.h (working copy) @@ -179,6 +179,16 @@ __in mxml_node_t *node ); +BOOLEAN PhUiUserStatusMessageCallback( + __in HWND hWnd, + __in_opt PWSTR Message, + __in NTSTATUS Status, + __in_opt ULONG Win32Result, + __in_opt PPH_STRING SystemMessage, + __in ULONG Type, + __out_opt PULONG_PTR Result + ); + PHAPPAPI VOID PhSearchOnlineString( __in HWND hWnd, Index: ProcessHacker/mainwnd.c =================================================================== --- ProcessHacker/mainwnd.c (revision 3954) +++ ProcessHacker/mainwnd.c (working copy) @@ -403,6 +403,7 @@ // Initialize child controls. PhMainWndOnCreate(); + PhSetUserStatusMessageCallback(PhUiUserStatusMessageCallback); PhpInitialLoadSettings(); PhLogInitialization(); PhQueueItemGlobalWorkQueue(PhpDelayedLoadFunction, NULL); Index: ProcessHacker/memedit.c =================================================================== --- ProcessHacker/memedit.c (revision 3954) +++ ProcessHacker/memedit.c (working copy) @@ -402,7 +402,7 @@ { if (offset >= context->RegionSize) { - PhShowError(hwndDlg, L"The offset is too large."); + PhShowError(hwndDlg, L"The offset is too large. Make sure it is less than the size of the region."); continue; } Index: ProcessHacker/settings.c =================================================================== --- ProcessHacker/settings.c (revision 3954) +++ ProcessHacker/settings.c (working copy) @@ -74,6 +74,7 @@ PhpAddStringSetting(L"DbgHelpSearchPath", L""); PhpAddIntegerSetting(L"DbgHelpUndecorate", L"1"); PhpAddIntegerSetting(L"ElevationLevel", L"1"); // PromptElevateAction + PhpAddIntegerSetting(L"EnableFriendly", L"0"); PhpAddIntegerSetting(L"EnableKph", L"1"); PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); PhpAddIntegerSetting(L"EnablePlugins", L"0");