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 | 305 +++++++++++++++++++++++++++++++++ include/roerrorapi.h | 3 + 3 files changed, 309 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..6c5cc2319bf 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -783,6 +783,310 @@ static void test_RoSetErrorReportingFlags(void) set_error_reporting_flags(RO_ERROR_REPORTING_USESETERRORINFO); }
+static void get_hresult_message(HRESULT code, WCHAR *buf, ULONG len) +{ + static const WCHAR *fallback = L"The text associated with this error code could not be found.\r\n"; + + if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buf, len, NULL)) + wcscpy(buf, fallback); +} + +#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]; + const WCHAR *str2; + IErrorInfo *info; + ULONG count, ctx; + + default_msg[0] = L'\0'; + 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); + ok_(__FILE__, line)(desc && !wcscmp(desc, default_msg), "Got desc %s != %s.\n", debugstr_w(desc), debugstr_w(default_msg)); + ok_(__FILE__, line)(code == exp_code, "Got unexpected code %#lx.\n", code); + 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)); + 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(hr == S_OK, "QueryInterface failed, hr %#lx.\n", hr); + + hr = IErrorInfo_GetGUID(info, &guid); + ok(hr == S_OK, "GetGUID failed, hr %#lx.\n", hr); + ok(IsEqualGUID(&guid, &GUID_NULL), "Got unexpected guid %s.\n", debugstr_guid(&guid)); + + desc = NULL; + hr = IErrorInfo_GetDescription(info, &desc); + ok(hr == S_OK, "GetDescription failed, hr %#lx.\n", hr); + ok(desc && !wcscmp(desc, default_msg), "GetDescription returned desc %s != %s\n", debugstr_w(desc), debugstr_w(default_msg)); + SysFreeString(desc); + + str = (BSTR)0xdeadbeef; + hr = IErrorInfo_GetSource(info, &str); + ok(hr == S_OK, "GetSource failed, hr %#lx.\n", hr); + ok(!str, "GetSource returned str %p.\n", debugstr_w(str)); + + str = (BSTR)0xdeadbeef; + hr = IErrorInfo_GetHelpFile(info, &str); + ok(hr == S_OK, "GetHelpFile failed, hr %#lx.\n", hr); + ok(!str, "GetHelpFile returned str %p.\n", debugstr_w(str)); + + ctx = 0xdeadbeef; + hr = IErrorInfo_GetHelpContext(info, &ctx); + ok(hr == S_OK, "GetHelpContext failed, hr %#lx.\n", hr); + ok(!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(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; + +static LONG WINAPI vectored_eh(EXCEPTION_POINTERS *ptr) +{ + const EXCEPTION_RECORD *rec = ptr->ExceptionRecord; + 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((HRESULT)rec->ExceptionInformation[0] == exp_hresult, "Got unexpected ExceptionInformation[0] %#Ix != %#lx.\n", + rec->ExceptionInformation[0], exp_hresult); + ok(rec->ExceptionInformation[1] == exp_len, "Got unexpected ExceptionInformation[1] %Iu != %lu.\n", + rec->ExceptionInformation[1], exp_len); + ok((!msg && !exp_msg) || (msg && exp_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'; + 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 +1098,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 | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+)
diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index 6c5cc2319bf..af4f4957410 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -783,6 +783,62 @@ 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); + 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); +} + static void get_hresult_message(HRESULT code, WCHAR *buf, ULONG len) { static const WCHAR *fallback = L"The text associated with this error code could not be found.\r\n"; @@ -1098,6 +1154,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 af4f4957410..991eb043120 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.rc | 29 ++++ dlls/combase/combase_private.h | 3 + dlls/combase/errorinfo.c | 33 ++-- dlls/combase/resource.h | 22 +++ dlls/combase/roapi.c | 302 ++++++++++++++++++++++++++++++++- dlls/combase/tests/roapi.c | 135 +++++++-------- 7 files changed, 435 insertions(+), 90 deletions(-) create mode 100644 dlls/combase/combase.rc create mode 100644 dlls/combase/resource.h
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.rc b/dlls/combase/combase.rc new file mode 100644 index 00000000000..c85e99031a7 --- /dev/null +++ b/dlls/combase/combase.rc @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "resource.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + IDS_FALLBACK_HRESULT_MESSAGE, "The text associated with this error code could not be found.\r\n" +} 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/resource.h b/dlls/combase/resource.h new file mode 100644 index 00000000000..34d698044fb --- /dev/null +++ b/dlls/combase/resource.h @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windef.h> +#include <winuser.h> + +#define IDS_FALLBACK_HRESULT_MESSAGE 1 diff --git a/dlls/combase/roapi.c b/dlls/combase/roapi.c index 5745099ae4f..4f2968e6fe0 100644 --- a/dlls/combase/roapi.c +++ b/dlls/combase/roapi.c @@ -26,7 +26,9 @@ #include "errhandlingapi.h"
#include "combase_private.h" +#include "resource.h"
+#include "wine/exception.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(combase); @@ -493,13 +495,226 @@ 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, const GUID *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) 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); + + if (!(desc = SysAllocString(impl->description))) return E_OUTOFMEMORY; + if (!(restricted_desc = SysAllocString(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, const GUID *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); + + if (!(*description = SysAllocString(impl->description))) return E_OUTOFMEMORY; + return S_OK; +} + +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 hr; }
/*********************************************************************** @@ -525,8 +740,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 +760,72 @@ 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, 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. */ + /* RO_ERROR_REPORTING_SUPPRESSSETERRORINFO overrides RO_ERROR_REPORTING_USESETERRORINFO */ + set_error = flags & RO_ERROR_REPORTING_USESETERRORINFO && !(flags & RO_ERROR_REPORTING_SUPPRESSSETERRORINFO); + /* RO_ERROR_REPORTING_FORCEEXCEPTIONS overrides RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS */ + raise_exception = (IsDebuggerPresent() && !(flags & RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS)) || + (flags & RO_ERROR_REPORTING_FORCEEXCEPTIONS); + /* No need to do anything if: + * No debugger is attached, and no error reporting flags are set, or + * A debugger is attached, but SetErrorInfo and exceptions are disabled. */ + if (!set_error && !raise_exception) return TRUE; + + if (!(len_desc = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, desc, ARRAY_SIZE(desc), NULL))) + len_desc = LoadStringW(hProxyDll, IDS_FALLBACK_HRESULT_MESSAGE, desc, ARRAY_SIZE(desc)); + + if (message) + { + /* Find the terminating NUL, and truncate the string to 512 characters. */ + max_len = max_len ? min(max_len, 512) : 512; + for (len_msg = 0; len_msg < max_len && message[len_msg]; len_msg++) /* nothing */; + } + else + len_msg = 0; + + if (set_error) + { + IErrorInfo *info = NULL; + + restricted_error_info_create(error, len_msg, message, len_desc, desc, &info); + /* Even if restricted_error_info_create fails, we should clear out the current IErrorInfo. */ + set_error_info(info); + ret = !!info; + } + 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 TRUE; + 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 991eb043120..c4f0f8b5067 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); @@ -808,32 +808,26 @@ static void test_GetRestrictedErrorInfo(void) 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); + 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); }
static void get_hresult_message(HRESULT code, WCHAR *buf, ULONG len) @@ -990,33 +984,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; @@ -1025,29 +1017,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; @@ -1055,27 +1046,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; @@ -1087,13 +1078,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); @@ -1104,11 +1095,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(); } @@ -1117,24 +1108,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);
Nikolay Sivov (@nsivov) commented about dlls/combase/combase_private.h:
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);
Why not use SetErrorInfo/GetErrorInfo directly?
Nikolay Sivov (@nsivov) commented about dlls/combase/combase.rc:
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
+#include "resource.h"
+#pragma makedep po
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+STRINGTABLE +{
- IDS_FALLBACK_HRESULT_MESSAGE, "The text associated with this error code could not be found.\r\n"
+}
I don't think this is necessary. Keeping English-only message is fine as a fallback. It doesn't help if exception data contains text about failure to populate exception data. Instead I think we should have something like a formatted string with "Error code 'code'".
Nikolay Sivov (@nsivov) commented about dlls/combase/roapi.c:
+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, const GUID *iid, void **out)
Normally we use REFIID, or const IID* in most places.
Nikolay Sivov (@nsivov) commented about dlls/combase/roapi.c:
+{
- 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) free(impl);
- return ref;
+}
Shouldn't this free strings too?
Nikolay Sivov (@nsivov) commented about dlls/combase/roapi.c:
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 hr;
This will return E_NOINTERFACE if a regular IErrorInfo was set. Is that expected? I don't see anything for that in tests.
Nikolay Sivov (@nsivov) commented about dlls/combase/roapi.c:
- UINT32 flags, len_msg, 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. */
- /* RO_ERROR_REPORTING_SUPPRESSSETERRORINFO overrides RO_ERROR_REPORTING_USESETERRORINFO */
- set_error = flags & RO_ERROR_REPORTING_USESETERRORINFO && !(flags & RO_ERROR_REPORTING_SUPPRESSSETERRORINFO);
- /* RO_ERROR_REPORTING_FORCEEXCEPTIONS overrides RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS */
- raise_exception = (IsDebuggerPresent() && !(flags & RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS)) ||
(flags & RO_ERROR_REPORTING_FORCEEXCEPTIONS);- /* No need to do anything if:
* No debugger is attached, and no error reporting flags are set, or* A debugger is attached, but SetErrorInfo and exceptions are disabled. */- if (!set_error && !raise_exception) return TRUE;
If this requires more comments than code lines, maybe it's possible to restructure it a bit, if it's not clear without comments.
Nikolay Sivov (@nsivov) commented about dlls/combase/roapi.c:
- /* RO_ERROR_REPORTING_FORCEEXCEPTIONS overrides RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS */
- raise_exception = (IsDebuggerPresent() && !(flags & RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS)) ||
(flags & RO_ERROR_REPORTING_FORCEEXCEPTIONS);- /* No need to do anything if:
* No debugger is attached, and no error reporting flags are set, or* A debugger is attached, but SetErrorInfo and exceptions are disabled. */- if (!set_error && !raise_exception) return TRUE;
- if (!(len_desc = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, desc, ARRAY_SIZE(desc), NULL)))
len_desc = LoadStringW(hProxyDll, IDS_FALLBACK_HRESULT_MESSAGE, desc, ARRAY_SIZE(desc));- if (message)
- {
/* Find the terminating NUL, and truncate the string to 512 characters. */max_len = max_len ? min(max_len, 512) : 512;for (len_msg = 0; len_msg < max_len && message[len_msg]; len_msg++) /* nothing */;
Is that some arbitrary length limit?
On Sat Nov 1 10:41:55 2025 +0000, Nikolay Sivov wrote:
Why not use SetErrorInfo/GetErrorInfo directly?
The build fails due to the linker getting those functions from ole32, which causes a duplicate symbol error :/
On Sat Nov 1 11:24:11 2025 +0000, Vibhav Pant wrote:
The build fails due to the linker getting those functions from ole32, which causes a duplicate symbol error :/
Ok. Then maybe having everything for error objects in the same will work. It's probably fine with those helpers too.
On Sat Nov 1 10:41:56 2025 +0000, Nikolay Sivov wrote:
Is that some arbitrary length limit?
Yes, the [MSDN docs for RoOriginateError(W)](https://learn.microsoft.com/en-us/windows/win32/api/roerrorapi/nf-roerrorapi...) state that the caller-provided message is truncated to 512 characters. We test for this in `test_RoOriginateError` as well: https://gitlab.winehq.org/wine/wine/-/blob/f9fca986dea77b7dc2527fd413008353f...
On Sat Nov 1 10:41:56 2025 +0000, Nikolay Sivov wrote:
If this requires more comments than code lines, maybe it's possible to restructure it a bit, if it's not clear without comments.
Hm, I added this to avoid called FormatMessageW if nothing needed to be done. I rewritten it to make this a little clearer. Is that better?