diff options
author | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-19 18:24:38 +0000 |
---|---|---|
committer | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-19 18:24:38 +0000 |
commit | f38b16fd2753b2d603e0a2a137227a132c77057b (patch) | |
tree | 47196cdec0752b96c5669830ba4ebc2bbad2c9ec /chrome | |
parent | 45aa74cc70c63a3a904007f9c84c1f73d9c2a35f (diff) | |
download | chromium_src-f38b16fd2753b2d603e0a2a137227a132c77057b.zip chromium_src-f38b16fd2753b2d603e0a2a137227a132c77057b.tar.gz chromium_src-f38b16fd2753b2d603e0a2a137227a132c77057b.tar.bz2 |
Adds 'hard error' message box support
- Initially just the two failed to delay-load bind exceptions
- Shows the same error dialog as the OS shows so no localization required
TEST= hard to test, but I'll give some pointers on the bug
BUG = 11919
Review URL: http://codereview.chromium.org/113492
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16391 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/breakpad_win.cc | 6 | ||||
-rw-r--r-- | chrome/app/chrome_exe.vcproj | 8 | ||||
-rw-r--r-- | chrome/app/hard_error_handler_win.cc | 122 | ||||
-rw-r--r-- | chrome/app/hard_error_handler_win.h | 36 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 |
5 files changed, 174 insertions, 0 deletions
diff --git a/chrome/app/breakpad_win.cc b/chrome/app/breakpad_win.cc index 725b9a7..b2644ba 100644 --- a/chrome/app/breakpad_win.cc +++ b/chrome/app/breakpad_win.cc @@ -16,6 +16,7 @@ #include "base/string_util.h" #include "base/win_util.h" #include "chrome/app/google_update_client.h" +#include "chrome/app/hard_error_handler_win.h" #include "chrome/common/env_vars.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/google_update_settings.h" @@ -119,6 +120,11 @@ struct CrashReporterInfo { bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*, EXCEPTION_POINTERS* ex_info, MDRawAssertionInfo*, bool) { + // If the exception is because there was a problem loading a delay-loaded + // module, then show the user a dialog explaining the problem and then exit. + if (DelayLoadFailureExceptionMessageBox(ex_info)) + return true; + // We set CHROME_CRASHED env var. If the CHROME_RESTART is present. // This signals the child process to show the 'chrome has crashed' dialog. if (!::GetEnvironmentVariableW(env_vars::kRestartInfo, NULL, 0)) diff --git a/chrome/app/chrome_exe.vcproj b/chrome/app/chrome_exe.vcproj index 7ac9ab4..d873dbb 100644 --- a/chrome/app/chrome_exe.vcproj +++ b/chrome/app/chrome_exe.vcproj @@ -223,6 +223,14 @@ > </File> <File + RelativePath=".\hard_error_handler_win.cc" + > + </File> + <File + RelativePath=".\hard_error_handler_win.h" + > + </File> + <File RelativePath="..\common\result_codes.h" > </File> diff --git a/chrome/app/hard_error_handler_win.cc b/chrome/app/hard_error_handler_win.cc new file mode 100644 index 0000000..77a8f81 --- /dev/null +++ b/chrome/app/hard_error_handler_win.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/app/hard_error_handler_win.h" + +#include <delayimp.h> +#include <ntsecapi.h> +#include <string> + +#include "base/basictypes.h" +#include "base/string_piece.h" +#include "base/sys_string_conversions.h" + +namespace { + +const int32 kExceptionModuleNotFound = VcppException(ERROR_SEVERITY_ERROR, + ERROR_MOD_NOT_FOUND); +const int32 kExceptionEntryPtNotFound = VcppException(ERROR_SEVERITY_ERROR, + ERROR_PROC_NOT_FOUND); + +const int32 NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139; +const int32 NT_STATUS_DLL_NOT_FOUND = 0xC0000135; + +bool MakeNTUnicodeString(const std::wstring& str, + UNICODE_STRING* nt_string) { + if (str.empty()) + return false; + uint32 str_size_bytes = str.size() * sizeof(wchar_t); + nt_string->Length = str_size_bytes; + nt_string->MaximumLength = str_size_bytes; + nt_string->Buffer = const_cast<wchar_t*>(str.c_str()); + return true; +} + +// NT-level function (not a win32 api) used to tell CSRSS of a critical error +// in the program which results in a message box dialog. +// The |exception| parameter is a standard exception code, the |param_count| +// indicates the number of items in |payload_params|. |payload_params| is +// dependent on the |exception| type but is typically an array to pointers to +// strings. |error_mode| indicates the kind of dialog buttons to show. +typedef LONG (WINAPI *NtRaiseHardErrorPF)(LONG exception, + ULONG param_count, + ULONG undocumented, + PVOID payload_params, + UINT error_mode, + PULONG response); + +// Helper function to call NtRaiseHardError(). It takes the exception code +// and one or two strings which are dependent on the exception code. No +// effort is done to validate that they match. +void RaiseHardErrorMsg(int32 exception, const std::wstring& text1, + const std::wstring& text2) { + // Bind the entry point. We can do it here since this function is really + // called at most once per session. Usually never called. + NtRaiseHardErrorPF NtRaiseHardError = + reinterpret_cast<NtRaiseHardErrorPF>( + ::GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtRaiseHardError")); + if (!NtRaiseHardError) + return; + + UNICODE_STRING uni_str1; + UNICODE_STRING uni_str2; + // A message needs to be displayed or else it would be confusing to the user. + if (!MakeNTUnicodeString(text1, &uni_str1)) + return; + int num_params = 1; + // The second string is optional. + if (MakeNTUnicodeString(text2, &uni_str2)) + num_params = 2; + + UNICODE_STRING* args[] = {&uni_str1, &uni_str2}; + uint32 undoc_value = 3; // Display message to user. + uint32 error_mode = 1; // Display OK button only. + ULONG response; // What user clicked in the dialog. Discarded. + NtRaiseHardError(exception, num_params, 3, args, error_mode, &response); +} + +} // namespace. + +// Using RaiseHardErrorMsg(), it generates the same message box that is seen +// when the loader cannot find a DLL that a module depends on. |module| is the +// DLL name and it cannot be empty. The Message box only has an 'ok' button. +void ModuleNotFoundHardError(const char* module) { + if (!module) + return; + std::wstring mod_name(base::SysMultiByteToWide(module, CP_ACP)); + RaiseHardErrorMsg(NT_STATUS_DLL_NOT_FOUND, mod_name, std::wstring()); +} + +// Using RaiseHardErrorMsg(), it generates the same message box that seen +// when the loader cannot find an import a module depends on. |module| is the +// DLL name and it cannot be empty. |entry| is the name of the method that +// could not be found. The Message box only has an 'ok' button. +void EntryPointNotFoundHardError(const char* entry, const char* module) { + if (!module || !entry) + return; + std::wstring entry_point(base::SysMultiByteToWide(entry, CP_ACP)); + std::wstring mod_name(base::SysMultiByteToWide(module, CP_ACP)); + RaiseHardErrorMsg(NT_STATUS_ENTRYPOINT_NOT_FOUND, entry_point, mod_name); +} + +bool DelayLoadFailureExceptionMessageBox(EXCEPTION_POINTERS* ex_info) { + if (!ex_info) + return false; + DelayLoadInfo* dli = reinterpret_cast<DelayLoadInfo*>( + ex_info->ExceptionRecord->ExceptionInformation[0]); + if (!dli) + return false; + if (ex_info->ExceptionRecord->ExceptionCode == kExceptionModuleNotFound) { + ModuleNotFoundHardError(dli->szDll); + return true; + } + if (ex_info->ExceptionRecord->ExceptionCode == kExceptionEntryPtNotFound) { + if (dli->dlp.fImportByName) { + EntryPointNotFoundHardError(dli->dlp.szProcName, dli->szDll); + return true; + } + } + return false; +} + diff --git a/chrome/app/hard_error_handler_win.h b/chrome/app/hard_error_handler_win.h new file mode 100644 index 0000000..51b16b45 --- /dev/null +++ b/chrome/app/hard_error_handler_win.h @@ -0,0 +1,36 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The routines in this file are windows-specific helper functions to deal +// with critical errors which the user can do something about. + +#ifndef CHROME_APP_HARD_ERROR_HANDLER_WIN_H_ +#define CHROME_APP_HARD_ERROR_HANDLER_WIN_H_ + +#include <windows.h> + +// Two exceptions can be thrown when delay-laoding DLLs: +// - Failed to load a DLL. The common reason is because it cannot be found. +// - Failed to bind to an entry point. Typically because the DLL is too old. +// These exceptions cause the termination of the program, but it is desirable +// to first inform the user the name of the module or the name of the entry +// point so he or she can get help. +// This function does exactly that. When an exception is captured and passed +// to this function, it will display a message box with the relevant +// information and return true if the exception is generated by the delay-load +// feature or else do nothing and return false. +bool DelayLoadFailureExceptionMessageBox(EXCEPTION_POINTERS* ex_info); + +// Generates a Popup dialog indicating that the entry point |entry| could +// not be found in dll |module|. The dialog is generated by CSRSS so this +// function can be called inside an exception handler. +void EntryPointNotFoundHardError(const char* entry, const char* module); + +// Generates a Popup dialog indicating that the dll |module| could not be found. +// The dialog is generated by CSRSS so this function can be called inside an +// exception handler. +void ModuleNotFoundHardError(const char* module); + +#endif // CHROME_APP_HARD_ERROR_HANDLER_WIN_H_ + diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 1ff13ed..be84b6a 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1899,6 +1899,8 @@ 'app/client_util.h', 'app/google_update_client.cc', 'app/google_update_client.h', + 'app/hard_error_handler_win.cc', + 'app/hard_error_handler_win.h', 'app/keystone_glue.h', 'app/keystone_glue.m', 'app/scoped_ole_initializer.h', |