Skip to content
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

Merged
merged 8 commits into from
May 28, 2019
Merged

Add RPC helpers #51

merged 8 commits into from
May 28, 2019

Conversation

jonwis
Copy link
Member

@jonwis jonwis commented May 27, 2019

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 and wil::invoke_rpc_result to convert raised exceptions into HRESULTs, both throwing and nonthrowing variants.

Also included are wil::unique_midl_ptr and wil::unique_midl_string for working with types returned by RPC calls allocated via MIDL_user_allocate.

@jonwis jonwis requested review from dunhor and ChrisGuzak May 27, 2019 00:22
@jonwis
Copy link
Member Author

jonwis commented May 27, 2019

Question for readers - since this is tied to Win32 RPC, should this instead go into win32_helpers.h instead of adding a completely new header?

@jonwis jonwis mentioned this pull request May 27, 2019
include/wil/resource.h Outdated Show resolved Hide resolved
include/wil/resource.h Outdated Show resolved Hide resolved
include/wil/rpc_helpers.h Outdated Show resolved Hide resolved
include/wil/rpc_helpers.h Outdated Show resolved Hide resolved
include/wil/rpc_helpers.h Outdated Show resolved Hide resolved
include/wil/rpc_helpers.h Outdated Show resolved Hide resolved
include/wil/rpc_helpers.h Outdated Show resolved Hide resolved
@jonwis jonwis merged commit 91c3565 into master May 28, 2019
@jonwis jonwis deleted the user/jonwis/rpc branch May 28, 2019 23:53
@oldnewthing
Copy link
Member

@jonwis Please update wiki.

@jonwis
Copy link
Member Author

jonwis commented Jun 12, 2019

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
Copy link
Member

@oldnewthing oldnewthing Jun 14, 2019

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #59 to resolve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants