123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- //*********************************************************
- //
- // Copyright (c) Microsoft. All rights reserved.
- // This code is licensed under the MIT License.
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- // PARTICULAR PURPOSE AND NONINFRINGEMENT.
- //
- //*********************************************************
- #ifndef __WIL_RPC_HELPERS_INCLUDED
- #define __WIL_RPC_HELPERS_INCLUDED
- #include "result.h"
- #include "resource.h"
- #include "wistd_functional.h"
- #include "wistd_type_traits.h"
- namespace wil
- {
- /// @cond
- namespace details
- {
- // This call-adapter template converts a void-returning 'wistd::invoke' into
- // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
- // with 'if constexpr' when C++17 is in wide use.
- template<typename TReturnType> struct call_adapter
- {
- template<typename... TArgs> static HRESULT call(TArgs&& ... args)
- {
- return wistd::invoke(wistd::forward<TArgs>(args)...);
- }
- };
- template<> struct call_adapter<void>
- {
- template<typename... TArgs> static HRESULT call(TArgs&& ... args)
- {
- wistd::invoke(wistd::forward<TArgs>(args)...);
- return S_OK;
- }
- };
- // Some RPC exceptions are already HRESULTs. Others are in the regular Win32
- // error space. If the incoming exception code isn't an HRESULT, wrap it.
- constexpr HRESULT map_rpc_exception(DWORD code)
- {
- return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
- }
- }
- /// @endcond
- /** Invokes an RPC method, mapping structured exceptions to HRESULTs
- Failures encountered by the RPC infrastructure (such as server crashes, authentication
- errors, client parameter issues, etc.) are emitted by raising a structured exception from
- within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
- RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
- flow control machinery to use.
- Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
- the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
- returned as-is.
- RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
- completes successfully.
- For example, consider an RPC interface method defined in idl as:
- ~~~
- HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
- ~~~
- To call this method, use:
- ~~~
- wil::unique_rpc_binding binding = // typically gotten elsewhere;
- wil::unique_midl_ptr<KittenState> state;
- HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
- RETURN_IF_FAILED(hr);
- ~~~
- */
- template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
- {
- RpcTryExcept
- {
- // Note: this helper type can be removed with C++17 enabled via
- // 'if constexpr(wistd::is_same_v<void, result_t>)'
- using result_t = typename wistd::__invoke_of<TCall...>::type;
- RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
- return S_OK;
- }
- RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
- {
- RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
- }
- RpcEndExcept
- }
- /** Invokes an RPC method, mapping structured exceptions to HRESULTs
- Failures encountered by the RPC infrastructure (such as server crashes, authentication
- errors, client parameter issues, etc.) are emitted by raising a structured exception from
- within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
- RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
- flow control machinery to use.
- Some RPC methods return results (such as a state enumeration or other value) directly in
- their signature. This adapter writes that result into a caller-provided object then
- returns S_OK.
- For example, consider an RPC interface method defined in idl as:
- ~~~
- GUID GetKittenId([in, ref, string] const wchar_t* name);
- ~~~
- To call this method, use:
- ~~~
- wil::unique_rpc_binding binding = // typically gotten elsewhere;
- GUID id;
- HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
- RETURN_IF_FAILED(hr);
- ~~~
- */
- template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... 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
- }
- namespace details
- {
- // Provides an adapter around calling the context-handle-close method on an
- // RPC interface, which itself is an RPC call.
- template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
- struct rpc_closer_t
- {
- static void Close(TStorage arg) WI_NOEXCEPT
- {
- LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
- }
- };
- }
- /** Manages explicit RPC context handles
- Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
- context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
- the server close out the context handle. As the close method itself is an RPC call,
- it can fail and raise a structured exception.
- This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
- helper, ensuring correct cleanup and lifecycle management.
- ~~~
- // Assume the interface has two methods:
- // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
- // HRESULT UseFoo([in] FOO_CONTEXT context;
- // void CloseFoo([in, out] PFOO_CONTEXT);
- using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
- unique_foo_context context;
- RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
- RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
- context.reset();
- ~~~
- */
- template<typename TContext, typename close_fn_t, close_fn_t close_fn>
- using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;
- #ifdef WIL_ENABLE_EXCEPTIONS
- /** Invokes an RPC method, mapping structured exceptions to C++ exceptions
- See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
- and those returned by the _method_ are mapped to HRESULTs and thrown inside a
- wil::ResultException. Using the example RPC method provided above:
- ~~~
- wil::unique_midl_ptr<KittenState> state;
- wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
- // use 'state'
- ~~~
- */
- template<typename... TCall> void invoke_rpc(TCall&& ... args)
- {
- THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
- }
- /** Invokes an RPC method, mapping structured exceptions to C++ exceptions
- See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
- _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
- example RPC method provided above:
- ~~~
- GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
- // use 'id'
- ~~~
- */
- template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
- {
- using result_t = typename wistd::__invoke_of<TCall...>::type;
- result_t result{};
- THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
- return result;
- }
- #endif
- }
- #endif
|