rpc_helpers.h 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // This code is licensed under the MIT License.
  5. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
  6. // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  7. // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  8. // PARTICULAR PURPOSE AND NONINFRINGEMENT.
  9. //
  10. //*********************************************************
  11. #ifndef __WIL_RPC_HELPERS_INCLUDED
  12. #define __WIL_RPC_HELPERS_INCLUDED
  13. #include "result.h"
  14. #include "resource.h"
  15. #include "wistd_functional.h"
  16. #include "wistd_type_traits.h"
  17. namespace wil
  18. {
  19. /// @cond
  20. namespace details
  21. {
  22. // This call-adapter template converts a void-returning 'wistd::invoke' into
  23. // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
  24. // with 'if constexpr' when C++17 is in wide use.
  25. template<typename TReturnType> struct call_adapter
  26. {
  27. template<typename... TArgs> static HRESULT call(TArgs&& ... args)
  28. {
  29. return wistd::invoke(wistd::forward<TArgs>(args)...);
  30. }
  31. };
  32. template<> struct call_adapter<void>
  33. {
  34. template<typename... TArgs> static HRESULT call(TArgs&& ... args)
  35. {
  36. wistd::invoke(wistd::forward<TArgs>(args)...);
  37. return S_OK;
  38. }
  39. };
  40. // Some RPC exceptions are already HRESULTs. Others are in the regular Win32
  41. // error space. If the incoming exception code isn't an HRESULT, wrap it.
  42. constexpr HRESULT map_rpc_exception(DWORD code)
  43. {
  44. return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
  45. }
  46. }
  47. /// @endcond
  48. /** Invokes an RPC method, mapping structured exceptions to HRESULTs
  49. Failures encountered by the RPC infrastructure (such as server crashes, authentication
  50. errors, client parameter issues, etc.) are emitted by raising a structured exception from
  51. within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
  52. RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
  53. flow control machinery to use.
  54. Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
  55. the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
  56. returned as-is.
  57. RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
  58. completes successfully.
  59. For example, consider an RPC interface method defined in idl as:
  60. ~~~
  61. HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
  62. ~~~
  63. To call this method, use:
  64. ~~~
  65. wil::unique_rpc_binding binding = // typically gotten elsewhere;
  66. wil::unique_midl_ptr<KittenState> state;
  67. HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
  68. RETURN_IF_FAILED(hr);
  69. ~~~
  70. */
  71. template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
  72. {
  73. RpcTryExcept
  74. {
  75. // Note: this helper type can be removed with C++17 enabled via
  76. // 'if constexpr(wistd::is_same_v<void, result_t>)'
  77. using result_t = typename wistd::__invoke_of<TCall...>::type;
  78. RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
  79. return S_OK;
  80. }
  81. RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
  82. {
  83. RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
  84. }
  85. RpcEndExcept
  86. }
  87. /** Invokes an RPC method, mapping structured exceptions to HRESULTs
  88. Failures encountered by the RPC infrastructure (such as server crashes, authentication
  89. errors, client parameter issues, etc.) are emitted by raising a structured exception from
  90. within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
  91. RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
  92. flow control machinery to use.
  93. Some RPC methods return results (such as a state enumeration or other value) directly in
  94. their signature. This adapter writes that result into a caller-provided object then
  95. returns S_OK.
  96. For example, consider an RPC interface method defined in idl as:
  97. ~~~
  98. GUID GetKittenId([in, ref, string] const wchar_t* name);
  99. ~~~
  100. To call this method, use:
  101. ~~~
  102. wil::unique_rpc_binding binding = // typically gotten elsewhere;
  103. GUID id;
  104. HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
  105. RETURN_IF_FAILED(hr);
  106. ~~~
  107. */
  108. template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
  109. {
  110. RpcTryExcept
  111. {
  112. result = wistd::invoke(wistd::forward<TCall>(args)...);
  113. return S_OK;
  114. }
  115. RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
  116. {
  117. RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
  118. }
  119. RpcEndExcept
  120. }
  121. namespace details
  122. {
  123. // Provides an adapter around calling the context-handle-close method on an
  124. // RPC interface, which itself is an RPC call.
  125. template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
  126. struct rpc_closer_t
  127. {
  128. static void Close(TStorage arg) WI_NOEXCEPT
  129. {
  130. LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
  131. }
  132. };
  133. }
  134. /** Manages explicit RPC context handles
  135. Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
  136. context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
  137. the server close out the context handle. As the close method itself is an RPC call,
  138. it can fail and raise a structured exception.
  139. This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
  140. helper, ensuring correct cleanup and lifecycle management.
  141. ~~~
  142. // Assume the interface has two methods:
  143. // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
  144. // HRESULT UseFoo([in] FOO_CONTEXT context;
  145. // void CloseFoo([in, out] PFOO_CONTEXT);
  146. using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
  147. unique_foo_context context;
  148. RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
  149. RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
  150. context.reset();
  151. ~~~
  152. */
  153. template<typename TContext, typename close_fn_t, close_fn_t close_fn>
  154. 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>;
  155. #ifdef WIL_ENABLE_EXCEPTIONS
  156. /** Invokes an RPC method, mapping structured exceptions to C++ exceptions
  157. See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
  158. and those returned by the _method_ are mapped to HRESULTs and thrown inside a
  159. wil::ResultException. Using the example RPC method provided above:
  160. ~~~
  161. wil::unique_midl_ptr<KittenState> state;
  162. wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
  163. // use 'state'
  164. ~~~
  165. */
  166. template<typename... TCall> void invoke_rpc(TCall&& ... args)
  167. {
  168. THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
  169. }
  170. /** Invokes an RPC method, mapping structured exceptions to C++ exceptions
  171. See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
  172. _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
  173. example RPC method provided above:
  174. ~~~
  175. GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
  176. // use 'id'
  177. ~~~
  178. */
  179. template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
  180. {
  181. using result_t = typename wistd::__invoke_of<TCall...>::type;
  182. result_t result{};
  183. THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
  184. return result;
  185. }
  186. #endif
  187. }
  188. #endif