Assuming I'm in a C++ program, I want to convert these reports to exceptions. Is using a C++ throw statement a reasonable way to do it, or am I stuck just redirecting to stderr?
No, you can not throw C++ exceptions from your hook.
It may work some of the time - but in general - when the hook is invoked the CRT is in an indeterminate state and may no longer be able to throw or handle exceptions. Throwing an exception when the CRT is in trouble, is a similar scenario to throwing an exception from the destructor of an object, that has been called during stack unwinding, due to an exception. Also, the depths of the CRT is not an appropriate place to throw C++ exceptions, doing so might leave the runtime in a bad state - if it wasn't already!
What you should do is the following:
int no_dialog_box_but_act_as_if_it_had_appeared_and_abort_was_clicked (int /* nRptType */,
char *szMsg,
int * /* retVal */)
{
fprintf (stderr, "CRT: %s\n", szMsg);
/* raise abort signal */
raise (SIGABRT);
/* We usually won't get here, but it's possible that
SIGABRT was ignored. So exit the program anyway. */
_exit (3);
}
Related
Reading the documentation it looks like the ShowWindow function has no notion of failure. This surprises me since it seems that pretty much any non-trivial code can fail.
The window handle might be invalid. Clearly, that's a contact violation committed by the caller but is this case simply "undefined" or "don't care", then?
I wonder if SetLastError is supported.
While ShowWindow() indeed has no notion of error, we can use SetWindowPos() as an alternative that is documented to support GetLastError().
In the following I provide an example that shows how to wrap SetWindowPos() into a function to bridge the gap between C-style error reporting and the C++ way of doing it by throwing and handling an exception.
Example:
#include <windows.h>
#include <iostream>
#include <sstream>
#include <system_error>
// Show or hide the given window by calling SetWindowPos().
//
// \exception Reports any error by throwing std::sytem_error exception.
void MyShowWindow( HWND hwnd, bool show ) {
DWORD flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER;
if( show )
flags |= SWP_SHOWWINDOW;
else
flags |= SWP_HIDEWINDOW;
if( !::SetWindowPos( hwnd, nullptr, 0, 0, 0, 0, flags ) ) {
// NOTE: Call GetLastError() IMMEDIATELY when a function's return value indicates
// failure and it is documented that said function supports GetLastError().
// ANY other code (be it your own or library code) before the next line must be
// avoided as it may invalidate the last error value.
DWORD err = ::GetLastError();
std::ostringstream msg;
msg << "Could not change visibility of window (HWND 0x" << hwnd << ")";
throw std::system_error( static_cast<int>( err ), std::system_category(), msg.str() );
}
}
Usage:
When using the wrapper function MyShowWindow() you must make sure to catch the exceptions thrown by it. The following example shows how to do that.
int main(){
try{
// Passing an invalid handle to MyShowWindow() will throw
// std::system_error exception. There may be other reasons for the
// function to fail, for instance if you pass the window handle of
// another process to which you don't have access as an argument
// to the function.
HWND anInvalidHandle = 0;
MyShowWindow( anInvalidHandle, true );
}
catch( std::system_error& e ){
// Catch the exception thrown by MyShowWindow() and report all
// available error details.
// e.code() outputs the error code retrieved via GetLastError().
std::cout << "Error: " << e.what() << std::endl
<< "Error code: " << e.code() << std::endl;
}
return 0;
}
Output:
Error: Could not change visibility of window (HWND 0x00000000): Ung³ltiges Fensterhandle
Error code: system:1400
The message says "invalid window handle", the error code corresponds to ERROR_INVALID_WINDOW_HANDLE.
Note:
Although the provided MyShowWindow() function only supports SW_HIDE and SW_SHOW functionality of ShowWindow, the remaining functionality could propably be provided by using additional SetWindowPos flags (e. g. SW_SHOWNA maps to SWP_SHOWWINDOW | SWP_NOACTIVATE) or calling other Windows API functions that provide this functionality and are documented to support GetLastError().
ShowWindowAsync while asynchronous in nature, it does however tell you if the operation was started successfully or not. Depending on what you are doing, it might be a usable alternative.
ShowWindow does not have any error-awareness. If the window provided does not exist (or is not accessible), it just returns false.
ShowWindow in fact does not do much more than sending a WM_SHOW message to the targeted window. Because of the nature of the windows message queue, ShowWindow has no knowledge about its completion status. Although, as pointed out in the comments, WM_SHOW is handled synchronously, the message queue itself has no built-in error reporting mechanism aside from sending error messages back to the sender.
[edit]
It seems that GetLastError reports an invalid window handle when trying to access a non-existing window. To me, this is an unknown behaviour as normally a return value should indicate whether to use GetLastError or not. However, this can be easily avoided by testing for the window manually in advance (see: IsWindow)
I run the following code in CLion:
int main()
{
char amessage [] = "oafaojfpa";
char * pmessage = "oafaojfpa";
char * apmessage = amessage;
amessage[2]='X';
*(pmessage+2)='X';
printf(amessage);
printf("\n");
printf(pmessage);
printf("\n");
printf(apmessage);
return(0);
}
The code *(pmessage+2)='X'; should raise exceptions. However, the output is:
/Users/spacegoing/Library/Caches/CLion12/cmake/generated/1ab7f406/1ab7f406/Debug/TCPL_Learn
Process finished with exit code 10
CLion only says exit code 10. But where can I view the exception message?
Only c++ code throws exceptions. In this case you are experiencing low level errors. You see a C/OS return value 10 which is BUS ERROR.
Bus errors are rare nowadays on x86 and occur when your processor cannot even attempt the memory access requested, typically:
using a processor instruction with an address that does not satisfy its alignment requirements.
modifying read only memory
Your pointer pmessage points to a string literal. This string is stored at read-only memory and trying to modify this memory leads to undefined behavior. It usually either segfaults or bus errors.
My C++ 2011 main() program for DiGSE is:
int main(int argc, char* argv[]) {
. . .
return EXIT_SUCCESS;
} // this } DOES match the opening { above
It compiles and executes correctly. A print statement immediately before the return outputs normally. However, a Windows 7.1 notification pops up saying "DiGSE.exe has stopped working." It then graciously offers to search the web for a solution.
I tried replacing the return with return 0; exit(0); and nothing so execution falls out the bottom (which, as I understand, is acceptable). However, in all cases I still get the pop-up.
What do I do to get the main() to exit gracefully?
DiGSE is just the name of the Windows 7 executable compiled on MinGW 4.9.2. The "full" program is already stripped down:
int main(int argc, char* argv[]) {
try {
DiGSE::log_init(DiGSE::log_dest_T::console_dest, "dig.log", true,
DiGSE::log_lvl_T::trace_lvl);
}//try
catch (const std::exception& ex) {
std::cerr << FMSG("\n"
"Executing '%1%' raised this exception:\n"
" %2%", % DiGSE::Partition::productName()
% ex.what())
<< std::endl;
return EXIT_FAILURE;
}//exception
catch (...) {
std::cerr << FMSG("\n"
"Executing '%1%' instance raised an unknown exception.",
% DiGSE::Partition::productName())
<< std::endl;
return EXIT_FAILURE;
}//exception
L_INFO(FMSG("'%1% v%2%' terminated normally.",
% DiGSE::Partition::productName()
% DiGSE::Partition::productVersion()))
return EXIT_SUCCESS;
}//main()
The L_INFO() is a logging call, which outputs as it should. The log_init() at the top initializes the log. Commenting out log_init() and L_INFO() has the same result as originally reported.
Program received signal SIGSEGV, Segmentation fault.
0x000000006fc8da9d in libstdc++-6!_ZNSo6sentryC1ERSo ()
from D:\Program Files\mingw-w64\x86_64-4.9.2-posix-seh-rt_v3-rev0\mingw64\bin
\libstdc++-6.dll
This is what gdb returns while mail() is exiting. It does this even with the log_init() and L_LNFO() commented out. So the problem is probably in one of globals of something it's linked to.
It is completely possible for a program to crash after the end of main -- the program isn't over yet. The following items execute after main() returns:
Registered at_exit handlers
Destructors for main()'s own automatic variables, and all variables with static storage duration (globals and function-static) (C++ only)
DllMain(PROCESS_DETACH) code in all dynamic libraries you are using (Windows only)
In addition to that, various events can occur outside your program and cause failures which you might mistake for a failure of your program (especially if your program forks or spawns copies of itself):
SIGCHLD is raised (on *nix). Process handles become signaled and cause wait functions to return (on Windows)
All open handles (file descriptors) get abandoned, and the close handler in the driver is invoked
The other end of connections (pipes, sockets) shift into a disconnected state (reads return 0, writes fail, on *nix SIGHUP may be raised)
I suggest attaching a debugger, set a breakpoint at the end of main, and then single-step through the cleanup code to find out where the failure is occurring. Divide and conquer may also be helpful (cut out some global variables, or all usage of a particular DLL).
I am using Mac OS 10.6.5, g++ 4.2.1. And meet problem with following code:
#include <iostream>
#include <sys/signal.h>
using namespace std;
void segfault_handler(int signum)
{
cout << "segfault caught!!!\n";
}
int main()
{
signal(SIGSEGV, segfault_handler);
int* p = 0;
*p = 100;
return 1;
}
It seems the segfault_handler is called infinitely and keep on print:
segfault caught!!!
segfault caught!!!
segfault caught!!!
...
I am new to Mac development, do you have any idea on what happened?
This is because after your signal handler executes, the EIP is back to the instruction which causes the SIGSEGV - so it executes again, and SIGSEGV is raised again.
Usually ignoring SIGSEGV like you do is meaningless anyway - suppose the instruction actually read some value from a pointer to a register, what would you do? You don't have any 'correct' value to put in the register, so the following code will likely SIGSEGV again or, worse, trigger some logic error.
You should either exit the process when SIGSEGV happens, or return to a known safe point - longjmp should work, if you know that this is indeed the safe point (the only possible example that comes to mind is VM interpreters/JITs).
Have you tried returning 0 instead of 1 in your program? Traditionally, values other than 0 indicate error. Also, does removing the two lines dealing with *p resolve it?
How can a Windows application handle segmentation faults? By 'handle' I mean intercept them and perhaps output a descriptive message. Also, the ability to recover from them would be nice too, but I assume that is too complicated.
Let them crash and let the Windows Error Reporting handle it - under Vista+, you should also consider registering with Restart Manager (http://msdn.microsoft.com/en-us/library/aa373347(VS.85).aspx), so that you have a chance to save out the user's work and restart the application (like what Word/Excel/etc.. does)
Use SEH for early exception handling,
and use SetUnhandledExceptionFilter to show a descriptive message.
If you add the /EHa compiler argument then try {} catch(...) will catch all exceptions for you, including SEH exceptions.
You can also use __try {} __except {} which gives you more flexibility on what to do when an exception is caught. putting an __try {} __except {} on your entire main() function is somewhat equivalent to using SetUnhandeledExceptionFilter().
That being said, you should also use the proper terminology: "seg-fault" is a UNIX term. There are no segmentation faults on Windows. On Windows they are called "Access Violation Exceptions"
C++ self-contained example on how to use SetUnhandledExceptionFilter, triggering a write fault and displaying a nice error message:
#include <windows.h>
#include <sstream>
LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
std::stringstream s;
s << "Fatal: Unhandled exception 0x" << std::hex << pExceptionInfo->ExceptionRecord->ExceptionCode
<< std::endl;
MessageBoxA(NULL, s.str().c_str(), "my application", MB_OK | MB_ICONSTOP);
exit(1);
return EXCEPTION_CONTINUE_SEARCH;
}
int main()
{
SetUnhandledExceptionFilter(TopLevelExceptionHandler);
int *v=0;
v[12] = 0; // should trigger the fault
return 0;
}
Tested successfully with g++ (and should work OK with MSVC++ as well)
What you want to do here depends on what sort of faults you are concerned with. If you have sloppy code that is prone to more or less random General Protection Violations, then #Paul Betts answer is what you need.
If you have code that has a good reason to deference bad pointers, and you want to recover, start from #whunmr's suggestion about SEH. You can handle and indeed recover, if you have clear enough control of your code to know exactly what state it is in at the point of the fault and how to go about recovering.
Similar to Jean-François Fabre solution, but with Posix code in MinGW-w64. But note that the program must exit - it can't recover from the SIGSEGV and continue.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sigHandler(int s)
{
printf("signal %d\n", s);
exit(1);
}
int main()
{
signal(SIGSEGV, sigHandler);
int *v=0;
*v = 0; // trigger the fault
return 0;
}