-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RPC helpers #51
Add RPC helpers #51
Conversation
Question for readers - since this is tied to Win32 RPC, should this instead go into |
@jonwis Please update wiki.
|
Added to the types lists and created https://github.com/microsoft/wil/wiki/RPC-helpers |
RETURN_IF_FAILED(hr); | ||
~~~ | ||
*/ | ||
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the TCall...
requires conversions to match the arguments of the invoked function, those conversions will take place inside the RpcTryExcept
, and if the conversions encounter C++ exceptions, crazy things will happen: The RpcExceptionFilter
will say "Sure, I'll handle that exception" and the function will simply return 0xE06d7363
instead of propagating the C++ exception.
We need to perform the conversions outside the RpcTryExcept
so the RPC exception handler won't swallow them.
One option is to convert it ourselves. This means that if an exception does occur, we will std::terminate
because this function is marked noexcept
.
template<typename TResult, typename... TExpectedArgs, typename... TActualArgs>
HRESULT invoke_rpc_result_nothrow(
TResult& result,
TResult(__stdcall*function)(TExpectedArgs...),
TActualArgs... args)
{
// C++ exception possible during conversion of arguments
std::tuple<TExpectedArgs...> converted{ std::forward<TActualArgs>(args)... };
RpcTryExcept
{
result = std::apply(function, std::move(converted));
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
Another option is to disallow conversions in the arguments and require the caller to preconvert them. This allows us to remain WI_NOEXCEPT
, and might even be more in the spirit of WIL: If you want the nothrow
version, you can't do things that might throw.
template<typename TResult, typename... TArgs>
HRESULT invoke_rpc_result_nothrow(
TResult& result,
TResult(__stdcall*function)(TArgs...),
TArgs... args) WI_NOEXCEPT
{
RpcTryExcept
{
result = wistd::invoke(wistd::forward<TCall>(args)...);
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
It does create extra work on the caller to do the preconversion.
extern HRESULT GetName(/* [out][string] */ LPWSTR* name);
wil::unique_midl_string name;
// does not compile
invoke_rpc_result_nothrow(hr, GetName, wil::out_param(name));
// you have to preconvert, which is cumbersome
invoke_rpc_result_nothrow(hr, GetName, static_cast<LPWSTR*>(wil::out_param(name)));
A third option is simply to document the limitation.
The behavior is undefined the conversion of the parameters to the types required by the RPC function results in a C++ exception or the allocation of temporaries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #59 to resolve.
This resolves issue #49.
See also MSDN's page on RPC exceptions. Structured exceptions and C++ exceptions don't play well together. This adds
wil::invoke_rpc
andwil::invoke_rpc_result
to convert raised exceptions into HRESULTs, both throwing and nonthrowing variants.Also included are
wil::unique_midl_ptr
andwil::unique_midl_string
for working with types returned by RPC calls allocated via MIDL_user_allocate.