diff --git a/dlls/kernel32/debugger.c b/dlls/kernel32/debugger.c index 5300fb530f2..34ef30ad6aa 100644 --- a/dlls/kernel32/debugger.c +++ b/dlls/kernel32/debugger.c @@ -28,6 +28,7 @@ #include "wine/server.h" #include "kernel_private.h" #include "wine/debug.h" +#include "wine/exception.h" WINE_DEFAULT_DEBUG_CHANNEL(debugstr); @@ -227,6 +228,11 @@ BOOL WINAPI DebugActiveProcessStop( DWORD pid ) return ret; } +static LONG WINAPI debug_exception_handler( EXCEPTION_POINTERS *eptr ) +{ + EXCEPTION_RECORD *rec = eptr->ExceptionRecord; + return (rec->ExceptionCode == DBG_PRINTEXCEPTION_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} /*********************************************************************** * OutputDebugStringA (KERNEL32.@) @@ -248,7 +254,22 @@ void WINAPI OutputDebugStringA( LPCSTR str ) if (!str) str = ""; + /* raise fake exception to make copy protections happy */ + __TRY + { + ULONG_PTR args[2]; + args[0] = strlen(str) + 1; + args[1] = (ULONG_PTR)str; + RaiseException( DBG_PRINTEXCEPTION_C, 0, 2, args ); + } + __EXCEPT(debug_exception_handler) + { + } + __ENDTRY + /* send string to attached debugger */ + /* FIXME should only send to debugger if exception is not caught by user-mode application */ + SERVER_START_REQ( output_debug_string ) { req->string = wine_server_client_ptr( str ); diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 8a80928954e..05bc15c4239 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1572,6 +1572,45 @@ static void test_dynamic_unwind(void) #endif /* __x86_64__ */ +static DWORD outputdebugstring_exceptions; + +static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); + + ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n", + rec->ExceptionCode, DBG_PRINTEXCEPTION_C); + ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + + outputdebugstring_exceptions++; + return EXCEPTION_CONTINUE_SEARCH; +} + +static void test_outputdebugstring(void) +{ + PVOID vectored_handler; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n"); + return; + } + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + outputdebugstring_exceptions = 0; + OutputDebugStringA("Hello World"); + ok(outputdebugstring_exceptions == 1, "OutputDebugStringA generated %d exceptions, expected one\n", + outputdebugstring_exceptions); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -1675,5 +1714,7 @@ START_TEST(exception) #endif + test_outputdebugstring(); + VirtualFree(code_mem, 0, MEM_FREE); } diff --git a/include/ntstatus.h b/include/ntstatus.h index 1eaae2d7a1f..9a7ca7a1125 100644 --- a/include/ntstatus.h +++ b/include/ntstatus.h @@ -1211,6 +1211,7 @@ #define DBG_TERMINATE_THREAD ((NTSTATUS) 0x40010003) #define DBG_TERMINATE_PROCESS ((NTSTATUS) 0x40010004) #define DBG_CONTROL_C ((NTSTATUS) 0x40010005) +#define DBG_PRINTEXCEPTION_C ((NTSTATUS) 0x40010006) #define DBG_CONTROL_BREAK ((NTSTATUS) 0x40010008) #define DBG_COMMAND_EXCEPTION ((NTSTATUS) 0x40010009) #define DBG_EXCEPTION_NOT_HANDLED ((NTSTATUS) 0x80010001) diff --git a/include/winnt.h b/include/winnt.h index 280cba0892c..e9e330dac75 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -622,6 +622,7 @@ typedef DWORD FLONG; #define DBG_TERMINATE_THREAD ((DWORD) 0x40010003) #define DBG_TERMINATE_PROCESS ((DWORD) 0x40010004) #define DBG_CONTROL_C ((DWORD) 0x40010005) +#define DBG_PRINTEXCEPTION_C ((DWORD) 0x40010006) #define DBG_CONTROL_BREAK ((DWORD) 0x40010008) #define DBG_COMMAND_EXCEPTION ((DWORD) 0x40010009) #define DBG_EXCEPTION_NOT_HANDLED ((DWORD) 0x80010001)