-- v3: combase: Implement RoOriginateError(W) and GetRestrictedErrorInfo. combase: Implement Ro{Get,Set}ErrorReportingFlags. combase/tests: Add tests for GetRestrictedErrorInfo. combase/tests: Add tests for RoOriginateError(W).
From: Vibhav Pant vibhavp@gmail.com
--- dlls/combase/tests/roapi.c | 145 +++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+)
diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index e4100cb11bc..6ba30ad87a7 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -639,6 +639,150 @@ static void test_RoGetErrorReportingFlags(void) ok(flags == RO_ERROR_REPORTING_USESETERRORINFO, "Got unexpected flag %#x.\n", flags); }
+static const RO_ERROR_REPORTING_FLAGS test_flags[] = { + RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS, + RO_ERROR_REPORTING_FORCEEXCEPTIONS, + RO_ERROR_REPORTING_FORCEEXCEPTIONS, + RO_ERROR_REPORTING_USESETERRORINFO, + RO_ERROR_REPORTING_SUPPRESSSETERRORINFO +}; + +#define set_error_reporting_flags(f) set_error_reporting_flags_(__LINE__, f) +static void set_error_reporting_flags_(int line, UINT32 flags) +{ + UINT32 new_flags = ~flags; + HRESULT hr; + + hr = RoSetErrorReportingFlags(flags); + ok_(__FILE__, line)(hr == S_OK, "RoSetErrorReportingFlags failed, hr %#lx.\n", hr); + hr = RoGetErrorReportingFlags(&new_flags); + ok_(__FILE__, line)(hr == S_OK, "RoGetErrorReportingFlags failed, hr %#lx.\n", hr); + todo_wine_if(flags != RO_ERROR_REPORTING_USESETERRORINFO) + ok_(__FILE__, line)(new_flags == flags, "Got unexpected flags %#x != %#x.\n", new_flags, flags); +} + +struct test_error_reporting_flags_params +{ + HANDLE event1; + HANDLE event2; +}; + +/* Flags are not apartment/thread local. */ +static DWORD CALLBACK test_thread_RoSetErrorReportingFlags(void *param) +{ + struct test_error_reporting_flags_params *data = param; + UINT32 flags = 0xdeadbeef; + HRESULT hr; + DWORD ret; + int i; + + hr = RoInitialize(RO_INIT_SINGLETHREADED); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(test_flags); i++) + { + winetest_push_context("flags=%#x", test_flags[i]); + ret = WaitForSingleObject(data->event1, 500); + ok(!ret, "WaitForSingleObject failed, error %lu.\n", ret); + + set_error_reporting_flags(test_flags[i]); + ret = SetEvent(data->event2); + ok(ret, "SetEvent failed, error %lu.\n", GetLastError()); + + /* Wait for the parent thread to reset flags to RO_ERROR_REPORTING_NONE. */ + ret = WaitForSingleObject(data->event1, 500); + ok(!ret, "WaitForSingleObject failed, error %lu.\n", ret); + flags = 0xdeadbeef; + hr = RoGetErrorReportingFlags(&flags); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + todo_wine ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#.x\n", flags); + winetest_pop_context(); + } + + RoUninitialize(); + return 0; +} + +static void test_RoSetErrorReportingFlags(void) +{ + struct test_error_reporting_flags_params data; + RO_INIT_TYPE type; + HANDLE thread; + UINT32 flags; + DWORD ret, i; + HRESULT hr; + + /* Pass non-existent flags */ + hr = RoSetErrorReportingFlags(RO_ERROR_REPORTING_USESETERRORINFO | 0x80); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + + set_error_reporting_flags(RO_ERROR_REPORTING_NONE); + + data.event1 = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!data.event1, "CreateEventW failed, error %lu.\n", GetLastError()); + data.event2 = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!data.event2, "CreateEventW failed, error %lu.\n", GetLastError()); + + for (type = RO_INIT_SINGLETHREADED; type < RO_INIT_MULTITHREADED; type++) + { + winetest_push_context("type=%d", type); + + thread = CreateThread(NULL, 0, test_thread_RoSetErrorReportingFlags, &data, 0, NULL); + ok(!!thread, "CreateThread failed, error %lu.\n", GetLastError()); + + for (i = 0; i < ARRAY_SIZE(test_flags); i++) + { + winetest_push_context("flags=%#x", test_flags[i]); + hr = RoInitialize(type); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + /* Flags don't change on apartment uninitialization.*/ + flags = 0xdeadbeef; + hr = RoGetErrorReportingFlags(&flags); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + todo_wine ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#x.\n", flags); + + ret = SetEvent(data.event1); + ok(ret, "SetEvent failed, error %lu.\n", GetLastError()); + /* Wait for the other thread to call RoSetErrorReportingFlags. */ + ret = WaitForSingleObject(data.event2, 500); + ok(!ret, "WaitForSingleObject failed, error %lu.\n", ret); + + /* RoSetErrorReportingFlags on the other thread should reflect here as well. */ + flags = 0xdeadbeef; + hr = RoGetErrorReportingFlags(&flags); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + todo_wine_if(test_flags[i] != RO_ERROR_REPORTING_USESETERRORINFO) + ok(flags == test_flags[i], "Got unexpected flags %#x\n", flags); + + /* Reset flags to RO_ERROR_REPORTING_NONE */ + set_error_reporting_flags(RO_ERROR_REPORTING_NONE); + ret = SetEvent(data.event1); + ok(ret, "SetEvent failed, error %lu.\n", GetLastError()); + + RoUninitialize(); + + /* Flags don't change on apartment uninitialization. */ + hr = RoGetErrorReportingFlags(&flags); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + todo_wine + ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#x.\n", flags); + winetest_pop_context(); + } + + ret = WaitForSingleObject(thread, 100); + ok(!ret, "WaitForSingleObject failed, error %lu.\n", ret); + CloseHandle(thread); + winetest_pop_context(); + } + + CloseHandle(data.event1); + CloseHandle(data.event2); + + /* Restore the default error reporting flags. */ + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); +} + START_TEST(roapi) { BOOL ret; @@ -649,6 +793,7 @@ START_TEST(roapi) test_ActivationFactories(); test_RoGetAgileReference(); test_RoGetErrorReportingFlags(); + test_RoSetErrorReportingFlags();
SetLastError(0xdeadbeef); ret = DeleteFileW(L"wine.combase.test.dll");
From: Vibhav Pant vibhavp@gmail.com
--- dlls/combase/tests/Makefile.in | 2 +- dlls/combase/tests/roapi.c | 324 +++++++++++++++++++++++++++++++++ include/roerrorapi.h | 3 + 3 files changed, 328 insertions(+), 1 deletion(-)
diff --git a/dlls/combase/tests/Makefile.in b/dlls/combase/tests/Makefile.in index 21597b38d51..ea76b746555 100644 --- a/dlls/combase/tests/Makefile.in +++ b/dlls/combase/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = combase.dll -IMPORTS = combase uuid user32 +IMPORTS = combase kernel32 oleaut32 uuid user32
SOURCES = \ combase.rc \ diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index 6ba30ad87a7..55470d35b2e 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -783,6 +783,329 @@ static void test_RoSetErrorReportingFlags(void) set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); }
+/* Return the system-supplied description of an HRESULT code. If there isn't one, we only check whether error + * descriptions are not empty strings. */ +static BOOL get_hresult_message(HRESULT code, WCHAR *buf, ULONG len) +{ + return !!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buf, len, NULL); +} + +#define test_IRestrictedErrorInfo(info, code, rest_desc, ref) \ + test_IRestrictedErrorInfo_(__LINE__, info, code, rest_desc, ref) +static void test_IRestrictedErrorInfo_(int line, IRestrictedErrorInfo *r_info, HRESULT exp_code, + const WCHAR *exp_rest_desc, const WCHAR *exp_ref) +{ + BSTR desc = NULL, rest_desc = NULL, sid = NULL, ref = NULL, str; + GUID guid = IID_IUnknown; + HRESULT hr, code = S_OK; + WCHAR default_msg[513]; + BOOL have_default_msg; + const WCHAR *str2; + IErrorInfo *info; + ULONG count, ctx; + + have_default_msg = get_hresult_message(exp_code, default_msg, ARRAY_SIZE(default_msg)); + + hr = IRestrictedErrorInfo_GetErrorDetails(r_info, &desc, &code, &rest_desc, &sid); + ok_(__FILE__, line)(hr == S_OK, "GetErrorDetails failed, hr %#lx.\n", hr); + if (have_default_msg) + ok_(__FILE__, line)(desc && !wcscmp(desc, default_msg), "Got desc %s != %s.\n", debugstr_w(desc), + debugstr_w(default_msg)); + else + ok_(__FILE__, line)(desc && wcslen(desc), "Got desc %s.\n", debugstr_w(desc)); + ok_(__FILE__, line)(code == exp_code, "Got unexpected code %#lx.\n", code); + if (exp_rest_desc || have_default_msg) + { + str2 = exp_rest_desc ? exp_rest_desc : default_msg; + ok_(__FILE__, line)(rest_desc && !wcscmp(rest_desc, str2), "Got rest_desc %s != %s.\n", debugstr_w(rest_desc), + debugstr_w(str2)); + } + else + ok_(__FILE__, line)(rest_desc && wcslen(rest_desc), "Got rest_desc %s.\n", debugstr_w(rest_desc)); + SysFreeString(desc); + SysFreeString(rest_desc); + + hr = IRestrictedErrorInfo_GetReference(r_info, &ref); + ok_(__FILE__, line)(hr == S_OK, "GetReference failed, hr %#lx.\n", hr); + ok_(__FILE__, line)((!ref && !exp_ref) || (ref && !wcscmp(ref, exp_ref)), "Got ref %s != %s.\n", debugstr_w(ref), + debugstr_w(exp_ref)); + SysFreeString(ref); + + /* IRestrictedErrorInfo objects also implement IErrorInfo. */ + hr = IRestrictedErrorInfo_QueryInterface(r_info, &IID_IErrorInfo, (void **)&info); + ok_(__FILE__, line)(hr == S_OK, "QueryInterface failed, hr %#lx.\n", hr); + + hr = IErrorInfo_GetGUID(info, &guid); + ok_(__FILE__, line)(hr == S_OK, "GetGUID failed, hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "Got unexpected guid %s.\n", debugstr_guid(&guid)); + + desc = NULL; + hr = IErrorInfo_GetDescription(info, &desc); + ok_(__FILE__, line)(hr == S_OK, "GetDescription failed, hr %#lx.\n", hr); + if (have_default_msg) + ok_(__FILE__, line)(desc && !wcscmp(desc, default_msg), "GetDescription returned desc %s != %s\n", + debugstr_w(desc), debugstr_w(default_msg)); + else + ok_(__FILE__, line)(desc && wcslen(desc), "GetDescription returned desc %s.\n", debugstr_w(desc)); + SysFreeString(desc); + + str = (BSTR)0xdeadbeef; + hr = IErrorInfo_GetSource(info, &str); + ok_(__FILE__, line)(hr == S_OK, "GetSource failed, hr %#lx.\n", hr); + ok_(__FILE__, line)(!str, "GetSource returned str %p.\n", debugstr_w(str)); + + str = (BSTR)0xdeadbeef; + hr = IErrorInfo_GetHelpFile(info, &str); + ok_(__FILE__, line)(hr == S_OK, "GetHelpFile failed, hr %#lx.\n", hr); + ok_(__FILE__, line)(!str, "GetHelpFile returned str %p.\n", debugstr_w(str)); + + ctx = 0xdeadbeef; + hr = IErrorInfo_GetHelpContext(info, &ctx); + ok_(__FILE__, line)(hr == S_OK, "GetHelpContext failed, hr %#lx.\n", hr); + ok_(__FILE__, line)(!ctx, "GetHelpContext returned ctx %#lx.\n", ctx); + + IErrorInfo_Release(info); + /* GetRestrictedErrorInfo transfers ownership of the object to the caller. */ + count = IRestrictedErrorInfo_Release(r_info); + ok_(__FILE__, line)(count == 0, "Got unexpected count %lu.\n", count); +} + +static BOOL exception_caught; +static HRESULT exp_hresult; +static ULONG exp_len; +static const WCHAR *exp_msg; +/* If FALSE, don't compare the message length and string for equality. */ +static BOOL have_default_msg; + +static LONG WINAPI vectored_eh(EXCEPTION_POINTERS *ptr) +{ + const EXCEPTION_RECORD *rec = ptr->ExceptionRecord; + const HRESULT hr = rec->ExceptionInformation[0]; + const ULONG_PTR len = rec->ExceptionInformation[1]; + const WCHAR *msg = (WCHAR *)rec->ExceptionInformation[2]; + + exception_caught = TRUE; + ok(rec->NumberParameters == 3, "Got unexpected NumberParameters %lu.\n", rec->NumberParameters); + ok(rec->ExceptionCode == EXCEPTION_RO_ORIGINATEERROR, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode); + ok(hr == exp_hresult, "Got unexpected ExceptionInformation[0] %#lx != %#lx.\n", hr, exp_hresult); + ok(len, "Got unexpected ExceptionInformation[1] %Iu.\n", len); + ok(msg && len == wcslen(msg), "Got unexpected ExceptionInformation[2] %s.\n", debugstr_w(msg)); + if (have_default_msg) + { + ok(len == exp_len, "Got unexpected ExceptionInformation[1] %Iu != %lu.\n", len, exp_len); + ok(msg && !wcscmp(msg, exp_msg), "Got unexpected ExceptionInformation[2] %s != %s.\n", debugstr_w(msg), + debugstr_w(exp_msg)); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +static void test_RoOriginateError(void) +{ + static const HRESULT test_codes[] = {E_INVALIDARG, E_FAIL, E_BOUNDS, E_NOINTERFACE, E_ABORT, E_CHANGED_STATE, RO_E_CLOSED, 0xdeffbeef}; + static const WCHAR message_nul[] = {'W', 'i', 'n', 'e', '\0', 'H', 'Q', '\0'}; + static const WCHAR *message = L"Wine is not an emulator."; + + WCHAR message_large[600], message_trunc[513], default_msg[513]; + const BOOL debugger = IsDebuggerPresent(); + IRestrictedErrorInfo *r_info; + void *handler; + HRESULT hr; + BOOL ret; + int i; + + /* RoOriginateError will only raise a structured exception if: + * A debugger is attached to the process, or + * RO_ERROR_REPORTING_FORCEEXCEPTIONS is set. + * In either case, setting RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS will not cause an exception to be raised. */ + handler = RtlAddVectoredExceptionHandler(1, vectored_eh); + ok(!!handler, "RtlAddVectoredExceptionHandler returned NULL.\n"); + + /* Using non-failure HRESULT values returns FALSE. No error information is set, and no exception is thrown. */ + exception_caught = FALSE; + ret = RoOriginateError(S_OK, NULL); + ok(!ret, "RoOriginateError returned %d.\n", ret); + ok(!exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_FORCEEXCEPTIONS); + exception_caught = FALSE; + ret = RoOriginateError(S_FALSE, NULL); + ok(!ret, "RoOriginateError returned %d.\n", ret); + ok(!exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + + memset(message_large, L'c', sizeof(message_large)); + message_large[ARRAY_SIZE(message_large) - 1] = L'\0'; + memcpy(message_trunc, message_large, sizeof(WCHAR) * 512); + message_trunc[512] = L'\0'; + + for (i = 0; i < ARRAY_SIZE(test_codes); i++) + { + HSTRING_BUFFER hstr_buf; + HSTRING_HEADER hstr_hdr; + HSTRING msg = NULL; + IErrorInfo *info; + WCHAR *buf; + + winetest_push_context("test_codes[%d]", i); + + default_msg[0] = L'\0'; + have_default_msg = get_hresult_message(test_codes[i], default_msg, ARRAY_SIZE(default_msg)); + + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); + exception_caught = FALSE; + exp_hresult = test_codes[i]; + exp_len = wcslen(default_msg); + exp_msg = default_msg[0] ? default_msg : NULL; + ret = RoOriginateError(test_codes[i], NULL); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + todo_wine_if(debugger) + ok(exception_caught == debugger, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL); + + /* A NULL string with a non-zero length is accepted. */ + exception_caught = FALSE; + ret = RoOriginateError(test_codes[i], NULL); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + todo_wine_if(debugger) + ok(exception_caught == debugger, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL); + + /* RO_ERROR_REPORTING_FORCEEXCEPTIONS overrides RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS */ + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS | + RO_ERROR_REPORTING_FORCEEXCEPTIONS); + exception_caught = FALSE; + ret = RoOriginateErrorW(test_codes[i], 0, NULL); + todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL); + + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); + exception_caught = FALSE; + exp_len = wcslen(message); + exp_msg = message; + /* RoOriginateError with a custom error message. */ + WindowsCreateStringReference(message, wcslen(message), &hstr_hdr, &msg); + ret = RoOriginateError(test_codes[i], msg); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + todo_wine_if(debugger) + ok(exception_caught == debugger, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL); + + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_FORCEEXCEPTIONS); + exception_caught = FALSE; + ret = RoOriginateErrorW(test_codes[i], wcslen(message), message); + todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL); + + exception_caught = FALSE; + ret = RoOriginateErrorW(test_codes[i], 0, message); + todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL); + + /* Error messages longer than 512 characters are truncated. */ + exception_caught = FALSE; + exp_len = wcslen(message_trunc); + exp_msg = message_trunc; + WindowsCreateStringReference(message_large, wcslen(message_large), &hstr_hdr, &msg); + ret = RoOriginateError(test_codes[i], msg); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL); + + exception_caught = FALSE; + ret = RoOriginateErrorW(test_codes[i], wcslen(message_large), message_large); + todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL); + + exception_caught = FALSE; + ret = RoOriginateErrorW(test_codes[i], 0, message_large); + todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL); + + /* RoOriginateError with a custom error message containing an embedded NUL. */ + exception_caught = FALSE; + exp_len = wcslen(message_nul); + exp_msg = message_nul; + hr = WindowsPreallocateStringBuffer(ARRAY_SIZE(message_nul), &buf, &hstr_buf); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + memcpy(buf, message_nul, sizeof(message_nul)); + hr = WindowsPromoteStringBuffer(hstr_buf, &msg); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ret = RoOriginateError(test_codes[i], msg); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + WindowsDeleteString(msg); + /* MSDN says that RoOriginateError uses SetErrorInfo to set the error object for the current thread, so we + * should be able to get the it through GetErrorInfo as well. */ + hr = GetErrorInfo(0, &info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + hr = IErrorInfo_QueryInterface(info, &IID_IRestrictedErrorInfo, (void **)&r_info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IErrorInfo_Release(info); + test_IRestrictedErrorInfo(r_info, test_codes[i], message_nul, NULL); + } + + exception_caught = FALSE; + ret = RoOriginateErrorW(test_codes[i], ARRAY_SIZE(message_nul), message_nul); + todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); + todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_nul, NULL); + + winetest_pop_context(); + } + + /* Disable SetErrorInfo, and suppress exceptions. */ + exception_caught = FALSE; + set_error_reporting_flags(RO_ERROR_REPORTING_NONE | RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS); + ret = RoOriginateError(E_FAIL, NULL); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + ok(!exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + r_info = (IRestrictedErrorInfo *)0xdeadbeef; + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + todo_wine ok(!r_info, "Got unexpected r_info %p\n", r_info); + ret = RtlRemoveVectoredExceptionHandler(handler); + ok(ret, "RtlRemoveVectoredExceptionHandler returned %d.\n", ret); + + /* RO_ERROR_REPORTING_SUPPRESSSETERRORINFO overrides RO_ERROR_REPORTING_USESETERRORINFO. */ + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_SUPPRESSSETERRORINFO); + ret = RoOriginateError(E_FAIL, NULL); + todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + todo_wine ok(!r_info, "Got unexpected r_info %p\n", r_info); + + /* Restore the default flags. */ + set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); +} + START_TEST(roapi) { BOOL ret; @@ -794,6 +1117,7 @@ START_TEST(roapi) test_RoGetAgileReference(); test_RoGetErrorReportingFlags(); test_RoSetErrorReportingFlags(); + test_RoOriginateError();
SetLastError(0xdeadbeef); ret = DeleteFileW(L"wine.combase.test.dll"); diff --git a/include/roerrorapi.h b/include/roerrorapi.h index 8f3200c559d..99ab59418be 100644 --- a/include/roerrorapi.h +++ b/include/roerrorapi.h @@ -32,6 +32,9 @@ typedef enum RO_ERROR_REPORTING_SUPPRESSSETERRORINFO = 0x8, } RO_ERROR_REPORTING_FLAGS;
+#define EXCEPTION_RO_ORIGINATEERROR 0x40080201 +#define EXCEPTION_RO_TRANSFORMERROR 0x40080202 + HRESULT WINAPI GetRestrictedErrorInfo(IRestrictedErrorInfo **info); HRESULT WINAPI RoGetErrorReportingFlags(UINT32 *flags); BOOL WINAPI RoOriginateError(HRESULT error, HSTRING message);
From: Vibhav Pant vibhavp@gmail.com
--- dlls/combase/tests/roapi.c | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+)
diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index 55470d35b2e..7efa89f5d7b 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -783,6 +783,63 @@ static void test_RoSetErrorReportingFlags(void) set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); }
+static void test_GetRestrictedErrorInfo(void) +{ + IRestrictedErrorInfo *r_info, *r_info2; + ICreateErrorInfo *create_info; + IErrorInfo *info; + ULONG count; + HRESULT hr; + BOOL ret; + + /* Clear the current error object, if any. */ + hr = SetErrorInfo(0, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + + /* The ICreateErrorInfo object returned by CreateErrorInfo does not supoprt IRestrictedErrorInfo. */ + hr = CreateErrorInfo(&create_info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ICreateErrorInfo_QueryInterface(create_info, &IID_IErrorInfo, (void **)&info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ICreateErrorInfo_Release(create_info); + hr = SetErrorInfo(0, info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + /* GetRestrictedErrorInfo should return S_FALSE if the error object does not support IRestrictedErrorInfo. */ + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + /* Nonetheless, GetRestrictedErrorInfo will still clear the current error. */ + count = IErrorInfo_Release(info); + todo_wine ok(count == 0, "Got unexpected count %lu.\n", count); + hr = GetErrorInfo(0, &info); + todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + if (hr == S_OK) IErrorInfo_Release(info); + + /* IRestrictedErrorInfo objects can only be created by the Ro* error reporting methods. */ + ret = RoOriginateError(E_INVALIDARG, NULL); + todo_wine ok(ret, "RoOriginateError failed.\n"); + hr = GetRestrictedErrorInfo(&r_info); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + hr = IRestrictedErrorInfo_QueryInterface(r_info, &IID_IErrorInfo, (void **)&info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IRestrictedErrorInfo_Release(r_info); + hr = SetErrorInfo(0, info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IErrorInfo_Release(info); + hr = GetRestrictedErrorInfo(&r_info2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(r_info2 == r_info, "Got unexpected r_info2 %p != %p.\n", r_info2, r_info); + count = IRestrictedErrorInfo_Release(r_info2); + ok(count == 0, "Got unexpected count %lu.\n", count); + } + else + SetErrorInfo(0, NULL); +} + /* Return the system-supplied description of an HRESULT code. If there isn't one, we only check whether error * descriptions are not empty strings. */ static BOOL get_hresult_message(HRESULT code, WCHAR *buf, ULONG len) @@ -1117,6 +1174,7 @@ START_TEST(roapi) test_RoGetAgileReference(); test_RoGetErrorReportingFlags(); test_RoSetErrorReportingFlags(); + test_GetRestrictedErrorInfo(); test_RoOriginateError();
SetLastError(0xdeadbeef);
From: Vibhav Pant vibhavp@gmail.com
--- dlls/combase/roapi.c | 13 ++++++++++--- dlls/combase/tests/roapi.c | 9 +++------ 2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/dlls/combase/roapi.c b/dlls/combase/roapi.c index a88181ada9b..5745099ae4f 100644 --- a/dlls/combase/roapi.c +++ b/dlls/combase/roapi.c @@ -547,12 +547,19 @@ HRESULT WINAPI RoReportUnhandledError(IRestrictedErrorInfo *info) return S_OK; }
+static LONG error_reporting_flags = RO_ERROR_REPORTING_USESETERRORINFO; /*********************************************************************** * RoSetErrorReportingFlags (combase.@) */ HRESULT WINAPI RoSetErrorReportingFlags(UINT32 flags) { - FIXME("(%08x): stub\n", flags); + UINT32 valid_flags = RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS | RO_ERROR_REPORTING_FORCEEXCEPTIONS | + RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_SUPPRESSSETERRORINFO; + + TRACE("(%08x)\n", flags); + + if (flags & ~valid_flags) return E_INVALIDARG; + WriteRelease(&error_reporting_flags, flags); return S_OK; }
@@ -561,12 +568,12 @@ HRESULT WINAPI RoSetErrorReportingFlags(UINT32 flags) */ HRESULT WINAPI RoGetErrorReportingFlags(UINT32 *flags) { - FIXME("(%p): stub\n", flags); + TRACE("(%p)\n", flags);
if (!flags) return E_POINTER;
- *flags = RO_ERROR_REPORTING_USESETERRORINFO; + *flags = ReadAcquire(&error_reporting_flags); return S_OK; }
diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index 7efa89f5d7b..1deb8c7c5e2 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -657,7 +657,6 @@ static void set_error_reporting_flags_(int line, UINT32 flags) ok_(__FILE__, line)(hr == S_OK, "RoSetErrorReportingFlags failed, hr %#lx.\n", hr); hr = RoGetErrorReportingFlags(&new_flags); ok_(__FILE__, line)(hr == S_OK, "RoGetErrorReportingFlags failed, hr %#lx.\n", hr); - todo_wine_if(flags != RO_ERROR_REPORTING_USESETERRORINFO) ok_(__FILE__, line)(new_flags == flags, "Got unexpected flags %#x != %#x.\n", new_flags, flags); }
@@ -695,7 +694,7 @@ static DWORD CALLBACK test_thread_RoSetErrorReportingFlags(void *param) flags = 0xdeadbeef; hr = RoGetErrorReportingFlags(&flags); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#.x\n", flags); + ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#.x\n", flags); winetest_pop_context(); }
@@ -714,7 +713,7 @@ static void test_RoSetErrorReportingFlags(void)
/* Pass non-existent flags */ hr = RoSetErrorReportingFlags(RO_ERROR_REPORTING_USESETERRORINFO | 0x80); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
set_error_reporting_flags(RO_ERROR_REPORTING_NONE);
@@ -740,7 +739,7 @@ static void test_RoSetErrorReportingFlags(void) flags = 0xdeadbeef; hr = RoGetErrorReportingFlags(&flags); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#x.\n", flags); + ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#x.\n", flags);
ret = SetEvent(data.event1); ok(ret, "SetEvent failed, error %lu.\n", GetLastError()); @@ -752,7 +751,6 @@ static void test_RoSetErrorReportingFlags(void) flags = 0xdeadbeef; hr = RoGetErrorReportingFlags(&flags); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine_if(test_flags[i] != RO_ERROR_REPORTING_USESETERRORINFO) ok(flags == test_flags[i], "Got unexpected flags %#x\n", flags);
/* Reset flags to RO_ERROR_REPORTING_NONE */ @@ -765,7 +763,6 @@ static void test_RoSetErrorReportingFlags(void) /* Flags don't change on apartment uninitialization. */ hr = RoGetErrorReportingFlags(&flags); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(flags == RO_ERROR_REPORTING_NONE, "Got unexpected flags %#x.\n", flags); winetest_pop_context(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/combase/Makefile.in | 1 + dlls/combase/combase_private.h | 3 + dlls/combase/errorinfo.c | 33 ++-- dlls/combase/roapi.c | 309 ++++++++++++++++++++++++++++++++- dlls/combase/tests/roapi.c | 137 +++++++-------- 5 files changed, 392 insertions(+), 91 deletions(-)
diff --git a/dlls/combase/Makefile.in b/dlls/combase/Makefile.in index f0acc33751f..41bcb0a16b7 100644 --- a/dlls/combase/Makefile.in +++ b/dlls/combase/Makefile.in @@ -7,6 +7,7 @@ DELAYIMPORTS = oleaut32 SOURCES = \ apartment.c \ combase.c \ + combase.rc \ dcom.idl \ errorinfo.c \ hglobalstream.c \ diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index ca79356e8eb..03c776c3935 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -261,3 +261,6 @@ HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, IID *iid, IUnknown **iface); HRESULT ipid_get_dest_context(const IPID *ipid, MSHCTX *dest_context, void **dest_context_data); HRESULT start_apartment_remote_unknown(struct apartment *apt); + +HRESULT set_error_info(IErrorInfo *error_info); +HRESULT get_error_info(IErrorInfo **error_info); diff --git a/dlls/combase/errorinfo.c b/dlls/combase/errorinfo.c index 9383e8d5a72..4ae9dfb1c5c 100644 --- a/dlls/combase/errorinfo.c +++ b/dlls/combase/errorinfo.c @@ -340,17 +340,14 @@ HRESULT WINAPI CreateErrorInfo(ICreateErrorInfo **ret) return S_OK; }
-/*********************************************************************** - * GetErrorInfo (combase.@) - */ -HRESULT WINAPI GetErrorInfo(ULONG reserved, IErrorInfo **error_info) +HRESULT get_error_info(IErrorInfo **error_info) { struct tlsdata *tlsdata; HRESULT hr;
- TRACE("%lu, %p\n", reserved, error_info); + TRACE("%p\n", error_info);
- if (reserved || !error_info) + if (!error_info) return E_INVALIDARG;
if (FAILED(hr = com_get_tlsdata(&tlsdata))) @@ -369,17 +366,20 @@ HRESULT WINAPI GetErrorInfo(ULONG reserved, IErrorInfo **error_info) }
/*********************************************************************** - * SetErrorInfo (combase.@) + * GetErrorInfo (combase.@) */ -HRESULT WINAPI SetErrorInfo(ULONG reserved, IErrorInfo *error_info) +HRESULT WINAPI GetErrorInfo(ULONG reserved, IErrorInfo **error_info) +{ + TRACE("%lu, %p\n", reserved, error_info); + return reserved ? E_INVALIDARG : get_error_info(error_info); +} + +HRESULT set_error_info(IErrorInfo *error_info) { struct tlsdata *tlsdata; HRESULT hr;
- TRACE("%lu, %p\n", reserved, error_info); - - if (reserved) - return E_INVALIDARG; + TRACE("%p\n", error_info);
if (FAILED(hr = com_get_tlsdata(&tlsdata))) return hr; @@ -393,3 +393,12 @@ HRESULT WINAPI SetErrorInfo(ULONG reserved, IErrorInfo *error_info)
return S_OK; } + +/*********************************************************************** + * SetErrorInfo (combase.@) + */ +HRESULT WINAPI SetErrorInfo(ULONG reserved, IErrorInfo *error_info) +{ + TRACE("%lu, %p\n", reserved, error_info); + return reserved ? E_INVALIDARG : set_error_info(error_info); +} diff --git a/dlls/combase/roapi.c b/dlls/combase/roapi.c index 5745099ae4f..57001eb14a4 100644 --- a/dlls/combase/roapi.c +++ b/dlls/combase/roapi.c @@ -27,6 +27,7 @@
#include "combase_private.h"
+#include "wine/exception.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(combase); @@ -493,13 +494,232 @@ HRESULT WINAPI RoRegisterActivationFactories(HSTRING *classes, PFNGETACTIVATIONF return S_OK; }
+struct restricted_error_info +{ + IRestrictedErrorInfo IRestrictedErrorInfo_iface; + IErrorInfo IErrorInfo_iface; + BSTR description; + BSTR restricted_description; + HRESULT code; + LONG ref; +}; + +static inline struct restricted_error_info *impl_from_IRestrictedErrorInfo(IRestrictedErrorInfo *iface) +{ + return CONTAINING_RECORD(iface, struct restricted_error_info, IRestrictedErrorInfo_iface); +} + +static HRESULT WINAPI restricted_error_info_QueryInterface(IRestrictedErrorInfo *iface, REFIID iid, void **out) +{ + struct restricted_error_info *impl = impl_from_IRestrictedErrorInfo(iface); + + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IRestrictedErrorInfo)) + { + IRestrictedErrorInfo_AddRef((*out = &impl->IRestrictedErrorInfo_iface)); + return S_OK; + } + if (IsEqualGUID(iid, &IID_IErrorInfo)) + { + IErrorInfo_AddRef((*out = &impl->IErrorInfo_iface)); + return S_OK; + } + + *out = NULL; + FIXME("%s not implemented, returning E_NOINTERFACE.", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI restricted_error_info_AddRef(IRestrictedErrorInfo *iface) +{ + struct restricted_error_info *impl = impl_from_IRestrictedErrorInfo(iface); + TRACE("(%p)\n", iface); + return InterlockedIncrement(&impl->ref); +} + +static ULONG WINAPI restricted_error_info_Release(IRestrictedErrorInfo *iface) +{ + struct restricted_error_info *impl = impl_from_IRestrictedErrorInfo(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + TRACE("(%p)\n", iface); + + if (!ref) + { + SysFreeString(impl->description); + SysFreeString(impl->restricted_description); + free(impl); + } + return ref; +} + +static HRESULT WINAPI restricted_error_info_GetErrorDetails(IRestrictedErrorInfo *iface, BSTR *ret_desc, HRESULT *code, + BSTR *ret_restricted_desc, BSTR *sid) +{ + struct restricted_error_info *impl = impl_from_IRestrictedErrorInfo(iface); + BSTR desc, restricted_desc; + + TRACE("(%p, %p, %p, %p, %p)\n", iface, ret_desc, code, ret_restricted_desc, sid); + + /* There are no terminating NUL characters, so we can use SysStringLen. */ + if (!(desc = SysAllocStringLen(impl->description, SysStringLen(impl->description)))) return E_OUTOFMEMORY; + if (!(restricted_desc = SysAllocStringLen(impl->restricted_description, SysStringLen(impl->restricted_description)))) + { + SysFreeString(desc); + return E_OUTOFMEMORY; + } + *code = impl->code; + *ret_desc = desc; + *ret_restricted_desc = restricted_desc; + + return S_OK; +} + +static HRESULT WINAPI restricted_error_info_GetReference(IRestrictedErrorInfo *iface, BSTR *reference) +{ + FIXME("(%p, %p): semi-stub!\n", iface, reference); + *reference = NULL; + return S_OK; +} + +static IRestrictedErrorInfoVtbl restricted_error_info_vtbl = +{ + /* IUnknown */ + restricted_error_info_QueryInterface, + restricted_error_info_AddRef, + restricted_error_info_Release, + /* IRestrictedErrorInfo */ + restricted_error_info_GetErrorDetails, + restricted_error_info_GetReference, +}; + +static inline struct restricted_error_info *impl_from_IErrorInfo(IErrorInfo *iface) +{ + return CONTAINING_RECORD(iface, struct restricted_error_info, IErrorInfo_iface); +} + +static HRESULT WINAPI error_info_QueryInterface(IErrorInfo *iface, REFIID iid, void **out) +{ + struct restricted_error_info *impl = impl_from_IErrorInfo(iface); + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + return IRestrictedErrorInfo_QueryInterface(&impl->IRestrictedErrorInfo_iface, iid, out); +} + +static ULONG WINAPI error_info_AddRef(IErrorInfo *iface) +{ + struct restricted_error_info *impl = impl_from_IErrorInfo(iface); + TRACE("(%p)\n", iface); + return IRestrictedErrorInfo_AddRef(&impl->IRestrictedErrorInfo_iface); +} + +static ULONG WINAPI error_info_Release(IErrorInfo *iface) +{ + struct restricted_error_info *impl = impl_from_IErrorInfo(iface); + TRACE("(%p)\n", iface); + return IRestrictedErrorInfo_Release(&impl->IRestrictedErrorInfo_iface); +} + +static HRESULT WINAPI error_info_GetDescription(IErrorInfo *iface, BSTR *description) +{ + struct restricted_error_info *impl = impl_from_IErrorInfo(iface); + + TRACE("(%p, %p)\n", iface, description); + + *description = SysAllocStringLen(impl->description, SysStringLen(impl->description)); + return *description ? S_OK : E_OUTOFMEMORY; +} + +static HRESULT WINAPI error_info_GetGUID(IErrorInfo *iface, GUID *guid) +{ + TRACE("(%p, %p)\n", iface, guid); + memset(guid, 0, sizeof(*guid)); + return S_OK; +} + +static HRESULT WINAPI error_info_GetHelpContext(IErrorInfo *iface, DWORD *context) +{ + TRACE("(%p, %p)\n", iface, context); + *context = 0; + return S_OK; +} + +static HRESULT WINAPI error_info_GetHelpFile(IErrorInfo *iface, BSTR *file) +{ + TRACE("(%p, %p)\n", iface, file); + *file = NULL; + return S_OK; +} + +static HRESULT WINAPI error_info_GetSource(IErrorInfo *iface, BSTR *source) +{ + TRACE("(%p, %p)\n", iface, source); + *source = NULL; + return S_OK; +} + +static const IErrorInfoVtbl error_info_vtbl = +{ + /* IUnknown */ + error_info_QueryInterface, + error_info_AddRef, + error_info_Release, + /* IErrorInfo */ + error_info_GetGUID, + error_info_GetSource, + error_info_GetDescription, + error_info_GetHelpFile, + error_info_GetHelpContext +}; + +HRESULT restricted_error_info_create(HRESULT code, ULONG len_msg, const WCHAR *message, ULONG len_desc, + const WCHAR *desc, IErrorInfo **info) +{ + struct restricted_error_info *impl; + + if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; + + impl->IRestrictedErrorInfo_iface.lpVtbl = &restricted_error_info_vtbl; + impl->IErrorInfo_iface.lpVtbl = &error_info_vtbl; + impl->code = code; + if (!(impl->description = SysAllocStringLen(desc, len_desc))) + { + free(impl); + return E_OUTOFMEMORY; + } + /* If the caller did not provide a message, use the description. */ + if (!len_msg) + { + message = impl->description; + len_msg = len_desc; + } + if (!(impl->restricted_description = SysAllocStringLen(message, len_msg))) + { + SysFreeString(impl->description); + free(impl); + return E_OUTOFMEMORY; + } + *info = &impl->IErrorInfo_iface; + return S_OK; +} + /*********************************************************************** * GetRestrictedErrorInfo (combase.@) */ HRESULT WINAPI GetRestrictedErrorInfo(IRestrictedErrorInfo **info) { - FIXME( "(%p)\n", info ); - return E_NOTIMPL; + IErrorInfo *error_info; + HRESULT hr; + + TRACE("(%p)\n", info); + + *info = NULL; + hr = get_error_info(&error_info); + if (hr != S_OK) return hr; + + hr = IErrorInfo_QueryInterface(error_info, &IID_IRestrictedErrorInfo, (void **)info); + IErrorInfo_Release(error_info); + return FAILED(hr) ? S_FALSE : S_OK; }
/*********************************************************************** @@ -525,8 +745,19 @@ BOOL WINAPI RoOriginateLanguageException(HRESULT error, HSTRING message, IUnknow */ BOOL WINAPI RoOriginateError(HRESULT error, HSTRING message) { - FIXME("%#lx, %s: stub\n", error, debugstr_hstring(message)); - return FALSE; + const WCHAR *buf; + UINT32 len; + + TRACE("%#lx, %s\n", error, debugstr_hstring(message)); + + buf = WindowsGetStringRawBuffer(message, &len); + return RoOriginateErrorW(error, len, buf); +} + +static LONG WINAPI rooriginate_handler(EXCEPTION_POINTERS *ptrs) +{ + EXCEPTION_RECORD *rec = ptrs->ExceptionRecord; + return (rec->ExceptionCode == EXCEPTION_RO_ORIGINATEERROR) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; }
/*********************************************************************** @@ -534,8 +765,74 @@ BOOL WINAPI RoOriginateError(HRESULT error, HSTRING message) */ BOOL WINAPI RoOriginateErrorW(HRESULT error, UINT max_len, const WCHAR *message) { - FIXME("%#lx, %u, %p: stub\n", error, max_len, message); - return FALSE; + BOOL set_error, raise_exception, ret = TRUE; + UINT32 flags, len_msg = 0, len_desc; + WCHAR desc[512]; + + TRACE("%#lx, %u, %p\n", error, max_len, message); + + if (SUCCEEDED(error)) return FALSE; + RoGetErrorReportingFlags(&flags); /* RoGetErrorReportingFlags is infalliable with a valid pointer. */ + /* We call SetErrorInfo if USESETERRORINFO is set and SUPPRESSSETERRORINFO is *not* set. */ + set_error = flags & RO_ERROR_REPORTING_USESETERRORINFO && !(flags & RO_ERROR_REPORTING_SUPPRESSSETERRORINFO); + /* We raise a structured exception if a debugger is present and SUPPRESSEXCEPTIONS is not set. + * However, FORCEEXCEPTIONS being set will always cause an exception to be raised. */ + raise_exception = (IsDebuggerPresent() && !(flags & RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS)) || + (flags & RO_ERROR_REPORTING_FORCEEXCEPTIONS); + if (set_error || raise_exception) + { + /* Get the HRESULT description. */ + if (!(len_desc = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, desc, ARRAY_SIZE(desc), NULL))) + len_desc = swprintf(desc, ARRAY_SIZE(desc), L"Error code '%#lx'.\r\n", error); + /* If the caller provided a message, find the terminating NUL and truncate it to 512 characters. */ + if (message) + { + max_len = max_len ? min(max_len, 512) : 512; + while (len_msg < max_len && message[len_msg]) len_msg++; + } + } + if (set_error) + { + IErrorInfo *info = NULL; + HRESULT hr; + + if (FAILED(restricted_error_info_create(error, len_msg, message, len_desc, desc, &info))) + ret = FALSE; + /* If restricted_error_info_create failed, this clears the current error object. */ + if (FAILED(hr = set_error_info(info))) + { + FIXME("Failed to set current error: %#lx\n", hr); + if (info) IErrorInfo_Release(info); + ret = FALSE; + } + } + if (raise_exception) + { + const WCHAR *src = len_msg ? message : desc; + ULONG len = len_msg ? len_msg : len_desc; + WCHAR *str; + + if (!(str = malloc(sizeof(WCHAR) * (len + 1)))) return ret; + memcpy(str, src, len * sizeof(WCHAR)); + str[len] = L'\0'; + + __TRY + { + ULONG_PTR args[3]; + + args[0] = error; + args[1] = len; + args[2] = (ULONG_PTR)str; + RaiseException(EXCEPTION_RO_ORIGINATEERROR, 0, 3, args); + } + __EXCEPT(rooriginate_handler) + { + } + __ENDTRY; + free(str); + } + + return ret; }
/*********************************************************************** diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index 1deb8c7c5e2..642bafa01f6 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -794,7 +794,7 @@ static void test_GetRestrictedErrorInfo(void) ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr);
/* The ICreateErrorInfo object returned by CreateErrorInfo does not supoprt IRestrictedErrorInfo. */ hr = CreateErrorInfo(&create_info); @@ -806,35 +806,29 @@ static void test_GetRestrictedErrorInfo(void) ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); /* GetRestrictedErrorInfo should return S_FALSE if the error object does not support IRestrictedErrorInfo. */ hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); /* Nonetheless, GetRestrictedErrorInfo will still clear the current error. */ count = IErrorInfo_Release(info); - todo_wine ok(count == 0, "Got unexpected count %lu.\n", count); + ok(count == 0, "Got unexpected count %lu.\n", count); hr = GetErrorInfo(0, &info); - todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); - if (hr == S_OK) IErrorInfo_Release(info); + ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr);
/* IRestrictedErrorInfo objects can only be created by the Ro* error reporting methods. */ ret = RoOriginateError(E_INVALIDARG, NULL); - todo_wine ok(ret, "RoOriginateError failed.\n"); + ok(ret, "RoOriginateError failed.\n"); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - hr = IRestrictedErrorInfo_QueryInterface(r_info, &IID_IErrorInfo, (void **)&info); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - IRestrictedErrorInfo_Release(r_info); - hr = SetErrorInfo(0, info); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - IErrorInfo_Release(info); - hr = GetRestrictedErrorInfo(&r_info2); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - ok(r_info2 == r_info, "Got unexpected r_info2 %p != %p.\n", r_info2, r_info); - count = IRestrictedErrorInfo_Release(r_info2); - ok(count == 0, "Got unexpected count %lu.\n", count); - } - else - SetErrorInfo(0, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IRestrictedErrorInfo_QueryInterface(r_info, &IID_IErrorInfo, (void **)&info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IRestrictedErrorInfo_Release(r_info); + hr = SetErrorInfo(0, info); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IErrorInfo_Release(info); + hr = GetRestrictedErrorInfo(&r_info2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(r_info2 == r_info, "Got unexpected r_info2 %p != %p.\n", r_info2, r_info); + count = IRestrictedErrorInfo_Release(r_info2); + ok(count == 0, "Got unexpected count %lu.\n", count); }
/* Return the system-supplied description of an HRESULT code. If there isn't one, we only check whether error @@ -1010,33 +1004,31 @@ static void test_RoOriginateError(void) exp_len = wcslen(default_msg); exp_msg = default_msg[0] ? default_msg : NULL; ret = RoOriginateError(test_codes[i], NULL); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); - todo_wine_if(debugger) + ok(ret, "RoOriginateError returned %d.\n", ret); ok(exception_caught == debugger, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL);
/* A NULL string with a non-zero length is accepted. */ exception_caught = FALSE; ret = RoOriginateError(test_codes[i], NULL); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); - todo_wine_if(debugger) + ok(ret, "RoOriginateError returned %d.\n", ret); ok(exception_caught == debugger, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL);
/* RO_ERROR_REPORTING_FORCEEXCEPTIONS overrides RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS */ set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS | RO_ERROR_REPORTING_FORCEEXCEPTIONS); exception_caught = FALSE; ret = RoOriginateErrorW(test_codes[i], 0, NULL); - todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateErrorW returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], NULL, NULL);
set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); exception_caught = FALSE; @@ -1045,29 +1037,28 @@ static void test_RoOriginateError(void) /* RoOriginateError with a custom error message. */ WindowsCreateStringReference(message, wcslen(message), &hstr_hdr, &msg); ret = RoOriginateError(test_codes[i], msg); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); - todo_wine_if(debugger) + ok(ret, "RoOriginateError returned %d.\n", ret); ok(exception_caught == debugger, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL);
set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_FORCEEXCEPTIONS); exception_caught = FALSE; ret = RoOriginateErrorW(test_codes[i], wcslen(message), message); - todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateErrorW returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL);
exception_caught = FALSE; ret = RoOriginateErrorW(test_codes[i], 0, message); - todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateErrorW returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message, NULL);
/* Error messages longer than 512 characters are truncated. */ exception_caught = FALSE; @@ -1075,27 +1066,27 @@ static void test_RoOriginateError(void) exp_msg = message_trunc; WindowsCreateStringReference(message_large, wcslen(message_large), &hstr_hdr, &msg); ret = RoOriginateError(test_codes[i], msg); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateError returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL);
exception_caught = FALSE; ret = RoOriginateErrorW(test_codes[i], wcslen(message_large), message_large); - todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateErrorW returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL);
exception_caught = FALSE; ret = RoOriginateErrorW(test_codes[i], 0, message_large); - todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateErrorW returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message_trunc, NULL);
/* RoOriginateError with a custom error message containing an embedded NUL. */ exception_caught = FALSE; @@ -1107,13 +1098,13 @@ static void test_RoOriginateError(void) hr = WindowsPromoteStringBuffer(hstr_buf, &msg); ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); ret = RoOriginateError(test_codes[i], msg); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateError returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); WindowsDeleteString(msg); /* MSDN says that RoOriginateError uses SetErrorInfo to set the error object for the current thread, so we * should be able to get the it through GetErrorInfo as well. */ hr = GetErrorInfo(0, &info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IErrorInfo_QueryInterface(info, &IID_IRestrictedErrorInfo, (void **)&r_info); @@ -1124,11 +1115,11 @@ static void test_RoOriginateError(void)
exception_caught = FALSE; ret = RoOriginateErrorW(test_codes[i], ARRAY_SIZE(message_nul), message_nul); - todo_wine ok(ret, "RoOriginateErrorW returned %d.\n", ret); - todo_wine ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); + ok(ret, "RoOriginateErrorW returned %d.\n", ret); + ok(exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) test_IRestrictedErrorInfo(r_info, test_codes[i], message_nul, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + test_IRestrictedErrorInfo(r_info, test_codes[i], message_nul, NULL);
winetest_pop_context(); } @@ -1137,24 +1128,24 @@ static void test_RoOriginateError(void) exception_caught = FALSE; set_error_reporting_flags(RO_ERROR_REPORTING_NONE | RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS); ret = RoOriginateError(E_FAIL, NULL); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + ok(ret, "RoOriginateError returned %d.\n", ret); ok(!exception_caught, "Got unexpected exception_caught %d.\n", exception_caught); r_info = (IRestrictedErrorInfo *)0xdeadbeef; hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!r_info, "Got unexpected r_info %p\n", r_info); + ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + ok(!r_info, "Got unexpected r_info %p\n", r_info); ret = RtlRemoveVectoredExceptionHandler(handler); ok(ret, "RtlRemoveVectoredExceptionHandler returned %d.\n", ret);
/* RO_ERROR_REPORTING_SUPPRESSSETERRORINFO overrides RO_ERROR_REPORTING_USESETERRORINFO. */ set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO | RO_ERROR_REPORTING_SUPPRESSSETERRORINFO); ret = RoOriginateError(E_FAIL, NULL); - todo_wine ok(ret, "RoOriginateError returned %d.\n", ret); + ok(ret, "RoOriginateError returned %d.\n", ret); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); hr = GetRestrictedErrorInfo(&r_info); - todo_wine ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(!r_info, "Got unexpected r_info %p\n", r_info); + ok(hr == S_FALSE, "Got unexpected hr %#lx.\n", hr); + ok(!r_info, "Got unexpected r_info %p\n", r_info);
/* Restore the default flags. */ set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO);
v3: * Use a simpler fallback string for unknown HRESULTs.
On Sat Nov 1 12:31:26 2025 +0000, Vibhav Pant wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/9332/diffs?diff_id=220379&start_sha=f9fca986dea77b7dc2527fd413008353f8329043#fe8d5f421f0a0f2eef3bb27caead7c50645a5dea_513_513)
Why would that be better? It's the same type, only hidden behind a typedef.
Mohamad Al-Jaf (@maljaf) commented about dlls/combase/Makefile.in:
SOURCES = \ apartment.c \ combase.c \
- combase.rc \
Leftover line of the deleted file.
On Sat Nov 1 21:37:24 2025 +0000, Mohamad Al-Jaf wrote:
Leftover line of the deleted file.
Oops, thanks!