/core/utility/error.h

https://github.com/stephenegriffin/mfcmapi · C Header · 436 lines · 263 code · 52 blank · 121 comment · 24 complexity · ff4bbb2eb162364b1fdfc5dc85e7623b MD5 · raw file

  1. #pragma once
  2. // Here's an index of error macros and functions for use throughout MFCMAPI
  3. // EC_H - Execute a function, log and return the HRESULT
  4. // Will display dialog on error
  5. // WC_H - Execute a function, log and return the HRESULT
  6. // Will not display an error dialog
  7. // EC_H_MSG - Execute a function, log error with uidErrorMessage, and return the HRESULT
  8. // Will display dialog on error
  9. // WC_H_MSG - Execute a function, log error with uidErrorMessage, and return the HRESULT
  10. // Will not display dialog on error
  11. // EC_MAPI Variant of EC_H that is only used to wrap MAPI api calls.
  12. // WC_MAPI Variant of WC_H that is only used to wrap MAPI api calls.
  13. // CHECKHRES - checks an hRes and logs and displays a dialog on error
  14. // CHECKHRESMSG - checks an hRes and logs and displays a dialog with a given error string on error
  15. // WARNHRESMSG - checks an hRes and logs a given error string on error
  16. // EC_W32 - does the same as EC_H, wrapping the result of the function call in HRESULT_FROM_WIN32
  17. // WC_W32 - does the same as WC_H, wrapping the result of the function call in HRESULT_FROM_WIN32
  18. // EC_B - Similar to EC_H, but for Boolean functions, using CheckWin32Error to set failure in hRes
  19. // WC_B - Similar to WC_H, but for Boolean functions, using CheckWin32Error to set failure in hRes
  20. // EC_D - Similar to EC_B, but preserves the result in _ret so it can be used later
  21. // WC_D - Similar to WC_B, but preserves the result in _ret so it can be used later
  22. // EC_H_GETPROPS - EC_H tuned for GetProps. Tosses all MAPI_W_ERRORS_RETURNED warnings.
  23. // WC_H_GETPROPS - WC_H tuned for GetProps. Tosses all MAPI_W_ERRORS_RETURNED warnings.
  24. // EC_H_CANCEL - EC_H tuned for functions which may be cancelled. Logs but does not display cancel errors.
  25. // No WC* macro needed here as WC_H already has the desired behavior.
  26. // EC_D_DIALOG - EC_H tuned for dialog functions that support CommDlgExtendedError
  27. // EC_PROBLEMARRAY - logs and displays dialog for any errors in a LPSPropProblemArray
  28. // EC_MAPIERR - logs and displays dialog for any errors in a LPMAPIERROR
  29. // EC_TNEFERR - logs and displays dialog for any errors in a LPSTnefProblemArray
  30. namespace error
  31. {
  32. extern std::function<void(const std::wstring& errString)> displayError;
  33. void LogFunctionCall(
  34. HRESULT hRes,
  35. HRESULT hrIgnore,
  36. bool bShowDialog,
  37. bool bMAPICall,
  38. bool bSystemCall,
  39. UINT uidErrorMsg,
  40. _In_opt_z_ LPCSTR szFunction,
  41. _In_z_ LPCSTR szFile,
  42. int iLine);
  43. void __cdecl ErrDialog(_In_z_ LPCSTR szFile, int iLine, UINT uidErrorFmt, ...);
  44. // Function to convert error codes to their names
  45. std::wstring ErrorNameFromErrorCode(ULONG hrErr);
  46. _Check_return_ HRESULT
  47. CheckWin32Error(bool bDisplayDialog, _In_z_ LPCSTR szFile, int iLine, _In_z_ LPCSTR szFunction);
  48. // Flag parsing array - used by ErrorNameFromErrorCode
  49. struct ERROR_ARRAY_ENTRY
  50. {
  51. ULONG ulErrorName;
  52. LPCWSTR lpszName;
  53. };
  54. typedef ERROR_ARRAY_ENTRY* LPERROR_ARRAY_ENTRY;
  55. inline _Check_return_ constexpr HRESULT CheckMe(const HRESULT hRes) noexcept { return hRes; }
  56. std::wstring ProblemArrayToString(_In_ const SPropProblemArray& problems);
  57. std::wstring MAPIErrToString(ULONG ulFlags, _In_ const MAPIERROR& err);
  58. std::wstring TnefProblemArrayToString(_In_ const STnefProblemArray& error);
  59. template <typename T> void CheckExtendedError(HRESULT hRes, T lpObject);
  60. } // namespace error
  61. // Macros for debug output
  62. #define CHECKHRES(hRes) (error::LogFunctionCall((hRes), NULL, true, false, false, NULL, nullptr, __FILE__, __LINE__))
  63. #define CHECKHRESMSG(hRes, uidErrorMsg) \
  64. (error::LogFunctionCall((hRes), NULL, true, false, false, (uidErrorMsg), nullptr, __FILE__, __LINE__))
  65. #define WARNHRESMSG(hRes, uidErrorMsg) \
  66. (error::LogFunctionCall((hRes), NULL, false, false, false, (uidErrorMsg), nullptr, __FILE__, __LINE__))
  67. // Execute a function, log and return the HRESULT
  68. // Will display dialog on error
  69. #define EC_H(fnx) \
  70. error::CheckMe([&]() -> HRESULT { \
  71. const auto __hRes = (fnx); \
  72. error::LogFunctionCall(__hRes, NULL, true, false, false, NULL, #fnx, __FILE__, __LINE__); \
  73. return __hRes; \
  74. }())
  75. // Execute a function, log and swallow the HRESULT
  76. // Will display dialog on error
  77. #define EC_H_S(fnx) \
  78. [&]() -> void { \
  79. const auto __hRes = (fnx); \
  80. error::LogFunctionCall(__hRes, NULL, true, false, false, NULL, #fnx, __FILE__, __LINE__); \
  81. }()
  82. // Execute a function, log and return the HRESULT
  83. // Will not display an error dialog
  84. #define WC_H(fnx) \
  85. error::CheckMe([&]() -> HRESULT { \
  86. const auto __hRes = (fnx); \
  87. error::LogFunctionCall(__hRes, NULL, false, false, false, NULL, #fnx, __FILE__, __LINE__); \
  88. return __hRes; \
  89. }())
  90. // Execute a function, log and swallow the HRESULT
  91. // Will not display an error dialog
  92. #define WC_H_S(fnx) \
  93. [&]() -> void { \
  94. const auto __hRes = (fnx); \
  95. error::LogFunctionCall(__hRes, NULL, false, false, false, NULL, #fnx, __FILE__, __LINE__); \
  96. }()
  97. // Execute a function, log and return the HRESULT
  98. // Logs a MAPI call trace under output::dbgLevel::MAPIFunctions
  99. // Will display dialog on error
  100. #define EC_MAPI(fnx) \
  101. error::CheckMe([&]() -> HRESULT { \
  102. const auto __hRes = (fnx); \
  103. error::LogFunctionCall(__hRes, NULL, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  104. return __hRes; \
  105. }())
  106. // Execute a function, log and swallow the HRESULT
  107. // Logs a MAPI call trace under output::dbgLevel::MAPIFunctions
  108. // Will display dialog on error
  109. #define EC_MAPI_S(fnx) \
  110. [&]() -> void { \
  111. const auto __hRes = (fnx); \
  112. error::LogFunctionCall(__hRes, NULL, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  113. }()
  114. // Execute a function, log and return the HRESULT
  115. // Logs a MAPI call trace under output::dbgLevel::MAPIFunctions
  116. // Will not display an error dialog
  117. #define WC_MAPI(fnx) \
  118. error::CheckMe([&]() -> HRESULT { \
  119. const auto __hRes = (fnx); \
  120. error::LogFunctionCall(__hRes, NULL, false, true, false, NULL, #fnx, __FILE__, __LINE__); \
  121. return __hRes; \
  122. }())
  123. // Execute a function, log and swallow the HRESULT
  124. // Logs a MAPI call trace under output::dbgLevel::MAPIFunctions
  125. // Will not display an error dialog
  126. #define WC_MAPI_S(fnx) \
  127. [&]() -> void { \
  128. const auto __hRes = (fnx); \
  129. error::LogFunctionCall(__hRes, NULL, false, true, false, NULL, #fnx, __FILE__, __LINE__); \
  130. }()
  131. // Execute a function, log error with uidErrorMessage, and return the HRESULT
  132. // Will display dialog on error
  133. #define EC_H_MSG(uidErrorMsg, fnx) \
  134. error::CheckMe([&]() -> HRESULT { \
  135. const auto __hRes = (fnx); \
  136. error::LogFunctionCall(__hRes, NULL, true, false, false, uidErrorMsg, #fnx, __FILE__, __LINE__); \
  137. return __hRes; \
  138. }())
  139. // Execute a function, log error with uidErrorMessage, and return the HRESULT
  140. // Will not display an error dialog
  141. #define WC_H_MSG(uidErrorMsg, fnx) \
  142. error::CheckMe([&]() -> HRESULT { \
  143. const auto __hRes = (fnx); \
  144. error::LogFunctionCall(__hRes, NULL, false, false, false, uidErrorMsg, #fnx, __FILE__, __LINE__); \
  145. return __hRes; \
  146. }())
  147. // Execute a function, log error with uidErrorMessage, and swallow the HRESULT
  148. // Will not display an error dialog
  149. #define WC_H_MSG_S(uidErrorMsg, fnx) \
  150. [&]() -> void { \
  151. const auto __hRes = (fnx); \
  152. error::LogFunctionCall(__hRes, NULL, false, false, false, uidErrorMsg, #fnx, __FILE__, __LINE__); \
  153. }()
  154. // Execute a W32 function which returns ERROR_SUCCESS on success, log error, and return HRESULT_FROM_WIN32
  155. // Will display dialog on error
  156. #define EC_W32(fnx) \
  157. error::CheckMe([&]() -> HRESULT { \
  158. const auto __hRes = HRESULT_FROM_WIN32(fnx); \
  159. error::LogFunctionCall(__hRes, NULL, true, false, false, NULL, #fnx, __FILE__, __LINE__); \
  160. return __hRes; \
  161. }())
  162. // Execute a W32 function which returns ERROR_SUCCESS on success, log error, and swallow error
  163. // Will display dialog on error
  164. #define EC_W32_S(fnx) \
  165. [&]() -> void { \
  166. const auto __hRes = HRESULT_FROM_WIN32(fnx); \
  167. error::LogFunctionCall(__hRes, NULL, true, false, false, NULL, #fnx, __FILE__, __LINE__); \
  168. }()
  169. // Execute a W32 function which returns ERROR_SUCCESS on success, log error, and return HRESULT_FROM_WIN32
  170. // Will not display an error dialog
  171. #define WC_W32(fnx) \
  172. error::CheckMe([&]() -> HRESULT { \
  173. const auto __hRes = HRESULT_FROM_WIN32(fnx); \
  174. error::LogFunctionCall(__hRes, NULL, false, false, false, NULL, #fnx, __FILE__, __LINE__); \
  175. return __hRes; \
  176. }())
  177. // Execute a W32 function which returns ERROR_SUCCESS on success, log error, and swallow error
  178. // Will not display an error dialog
  179. #define WC_W32_S(fnx) \
  180. [&]() -> void { \
  181. const auto __hRes = HRESULT_FROM_WIN32(fnx); \
  182. error::LogFunctionCall(__hRes, NULL, false, false, false, NULL, #fnx, __FILE__, __LINE__); \
  183. }()
  184. // Execute a bool/BOOL function, log error, and return CheckWin32Error(HRESULT)
  185. // Will display dialog on error
  186. #define EC_B(fnx) \
  187. error::CheckMe( \
  188. [&]() -> HRESULT { return !(fnx) ? error::CheckWin32Error(true, __FILE__, __LINE__, #fnx) : S_OK; }())
  189. // Execute a bool/BOOL function, log error, and swallow error
  190. // Will display dialog on error
  191. #define EC_B_S(fnx) \
  192. [&]() -> void { \
  193. if (!(fnx)) \
  194. { \
  195. static_cast<void>(error::CheckWin32Error(true, __FILE__, __LINE__, #fnx)); \
  196. } \
  197. }()
  198. // Execute a bool/BOOL function, log error, and return CheckWin32Error(HRESULT)
  199. // Will not display an error dialog
  200. #define WC_B(fnx) \
  201. error::CheckMe( \
  202. [&]() -> HRESULT { return !(fnx) ? error::CheckWin32Error(false, __FILE__, __LINE__, #fnx) : S_OK; }())
  203. // Execute a bool/BOOL function, log error, and swallow error
  204. // Will not display an error dialog
  205. #define WC_B_S(fnx) \
  206. [&]() -> void { \
  207. if (!(fnx)) \
  208. { \
  209. static_cast<void>(error::CheckWin32Error(false, __FILE__, __LINE__, #fnx)); \
  210. } \
  211. }()
  212. // Execute a function which returns 0 on error, log error, and return result
  213. // Will display dialog on error
  214. #define EC_D(_TYPE, fnx) \
  215. [&]() -> _TYPE { \
  216. const auto __ret = (fnx); \
  217. if (!__ret) \
  218. { \
  219. static_cast<void>(error::CheckWin32Error(true, __FILE__, __LINE__, #fnx)); \
  220. } \
  221. return __ret; \
  222. }()
  223. // Execute a function which returns 0 on error, log error, and return CheckWin32Error(HRESULT)
  224. // Will not display an error dialog
  225. #define WC_D(_TYPE, fnx) \
  226. [&]() -> _TYPE { \
  227. const auto __ret = (fnx); \
  228. if (!__ret) \
  229. { \
  230. static_cast<void>(error::CheckWin32Error(false, __FILE__, __LINE__, #fnx)); \
  231. } \
  232. return __ret; \
  233. }()
  234. // Execute a function which returns 0 on error, log error, and swallow error
  235. // Will not display an error dialog
  236. #define WC_D_S(fnx) \
  237. [&]() -> void { \
  238. if (!(fnx)) \
  239. { \
  240. static_cast<void>(error::CheckWin32Error(false, __FILE__, __LINE__, #fnx)); \
  241. } \
  242. }()
  243. // Execute a function, log and return the HRESULT
  244. // Does not log/display on error if it matches __ignore
  245. // Does not suppress __ignore as return value so caller can check it
  246. // Will display dialog on error
  247. #define EC_H_IGNORE_RET(__ignore, fnx) \
  248. error::CheckMe([&]() -> HRESULT { \
  249. const auto __hRes = (fnx); \
  250. error::LogFunctionCall(__hRes, __ignore, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  251. return __hRes; \
  252. }())
  253. // Execute a function, log and return the HRESULT
  254. // MAPI's GetProps call will return MAPI_W_ERRORS_RETURNED if even one prop fails
  255. // This is annoying, so this macro tosses those warnings.
  256. // We have to check each prop before we use it anyway, so we don't lose anything here.
  257. // Using this macro, all we have to check is that we got a props array back
  258. // Will display dialog on error
  259. #define EC_H_IGNORE(__ignore, fnx) \
  260. error::CheckMe([&]() -> HRESULT { \
  261. const auto __hRes = (fnx); \
  262. error::LogFunctionCall(__hRes, __ignore, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  263. return __hRes == (__ignore) ? S_OK : __hRes; \
  264. }())
  265. #define EC_H_GETPROPS(fnx) EC_H_IGNORE(MAPI_W_ERRORS_RETURNED, fnx)
  266. // Execute a function, log and swallow the HRESULT
  267. // MAPI's GetProps call will return MAPI_W_ERRORS_RETURNED if even one prop fails
  268. // This is annoying, so this macro tosses those warnings.
  269. // We have to check each prop before we use it anyway, so we don't lose anything here.
  270. // Using this macro, all we have to check is that we got a props array back
  271. // Will display dialog on error
  272. #define EC_H_IGNORE_S(__ignore, fnx) \
  273. [&]() -> void { \
  274. const auto __hRes = (fnx); \
  275. error::LogFunctionCall(__hRes, __ignore, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  276. }()
  277. #define EC_H_GETPROPS_S(fnx) EC_H_IGNORE_S(MAPI_W_ERRORS_RETURNED, fnx)
  278. // Execute a function, log and return the HRESULT
  279. // MAPI's GetProps call will return MAPI_W_ERRORS_RETURNED if even one prop fails
  280. // This is annoying, so this macro tosses those warnings.
  281. // We have to check each prop before we use it anyway, so we don't lose anything here.
  282. // Using this macro, all we have to check is that we got a props array back
  283. // Will not display an error dialog
  284. #define WC_H_IGNORE(__ignore, fnx) \
  285. error::CheckMe([&]() -> HRESULT { \
  286. const auto __hRes = (fnx); \
  287. error::LogFunctionCall(__hRes, __ignore, false, true, false, NULL, #fnx, __FILE__, __LINE__); \
  288. return __hRes == (__ignore) ? S_OK : __hRes; \
  289. }())
  290. #define WC_H_GETPROPS(fnx) WC_H_IGNORE(MAPI_W_ERRORS_RETURNED, fnx)
  291. // Execute a function, log and swallow the HRESULT
  292. // MAPI's GetProps call will return MAPI_W_ERRORS_RETURNED if even one prop fails
  293. // This is annoying, so this macro tosses those warnings.
  294. // We have to check each prop before we use it anyway, so we don't lose anything here.
  295. // Using this macro, all we have to check is that we got a props array back
  296. // Will not display an error dialog
  297. #define WC_H_IGNORE_S(__ignore, fnx) \
  298. [&]() -> void { \
  299. const auto __hRes = (fnx); \
  300. error::LogFunctionCall(__hRes, __ignore, false, true, false, NULL, #fnx, __FILE__, __LINE__); \
  301. }()
  302. #define WC_H_GETPROPS_S(fnx) WC_H_IGNORE_S(MAPI_W_ERRORS_RETURNED, fnx)
  303. // Execute a function, log and return the HRESULT
  304. // Logs a MAPI call trace under output::dbgLevel::MAPIFunctions
  305. // Some MAPI functions allow MAPI_E_CANCEL or MAPI_E_USER_CANCEL.
  306. // I don't consider these to be errors.
  307. // Will display dialog on error
  308. #define EC_H_CANCEL(fnx) \
  309. error::CheckMe([&]() -> HRESULT { \
  310. const auto __hRes = (fnx); \
  311. if (__hRes == MAPI_E_USER_CANCEL || __hRes == MAPI_E_CANCEL) \
  312. { \
  313. error::LogFunctionCall(__hRes, NULL, false, true, false, IDS_USERCANCELLED, #fnx, __FILE__, __LINE__); \
  314. return S_OK; \
  315. } \
  316. else \
  317. error::LogFunctionCall(__hRes, NULL, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  318. return __hRes; \
  319. }())
  320. // Execute a function, log and swallow the HRESULT
  321. // Logs a MAPI call trace under output::dbgLevel::MAPIFunctions
  322. // Some MAPI functions allow MAPI_E_CANCEL or MAPI_E_USER_CANCEL.
  323. // I don't consider these to be errors.
  324. // Will display dialog on error
  325. #define EC_H_CANCEL_S(fnx) \
  326. [&]() -> void { \
  327. const auto __hRes = (fnx); \
  328. if (__hRes == MAPI_E_USER_CANCEL || __hRes == MAPI_E_CANCEL) \
  329. error::LogFunctionCall(__hRes, NULL, false, true, false, IDS_USERCANCELLED, #fnx, __FILE__, __LINE__); \
  330. else \
  331. error::LogFunctionCall(__hRes, NULL, true, true, false, NULL, #fnx, __FILE__, __LINE__); \
  332. }()
  333. // Execute a function, log and return the command ID
  334. // Designed to check return values from dialog functions, primarily DoModal
  335. // These functions use CommDlgExtendedError to get error information
  336. // Will display dialog on error
  337. #define EC_D_DIALOG(fnx) \
  338. [&]() -> INT_PTR { \
  339. const auto __iDlgRet = (fnx); \
  340. if (IDCANCEL == __iDlgRet) \
  341. { \
  342. const auto __err = CommDlgExtendedError(); \
  343. if (__err) \
  344. { \
  345. error::ErrDialog(__FILE__, __LINE__, IDS_EDCOMMONDLG, #fnx, __err); \
  346. } \
  347. } \
  348. return __iDlgRet; \
  349. }()
  350. #define EC_PROBLEMARRAY(problemarray) \
  351. { \
  352. if (problemarray) \
  353. { \
  354. const std::wstring szProbArray = error::ProblemArrayToString(*(problemarray)); \
  355. error::ErrDialog(__FILE__, __LINE__, IDS_EDPROBLEMARRAY, szProbArray.c_str()); \
  356. output::DebugPrint(output::dbgLevel::Generic, L"Problem array:\n%ws\n", szProbArray.c_str()); \
  357. } \
  358. }
  359. #define WC_PROBLEMARRAY(problemarray) \
  360. { \
  361. if (problemarray) \
  362. { \
  363. const std::wstring szProbArray = error::ProblemArrayToString(*(problemarray)); \
  364. output::DebugPrint(output::dbgLevel::Generic, L"Problem array:\n%ws\n", szProbArray.c_str()); \
  365. } \
  366. }
  367. #define EC_MAPIERR(__ulflags, __lperr) \
  368. { \
  369. if (__lperr) \
  370. { \
  371. const std::wstring szErr = error::MAPIErrToString((__ulflags), *(__lperr)); \
  372. error::ErrDialog(__FILE__, __LINE__, IDS_EDMAPIERROR, szErr.c_str()); \
  373. output::DebugPrint(output::dbgLevel::Generic, L"LPMAPIERROR:\n%ws\n", szErr.c_str()); \
  374. } \
  375. }
  376. #define EC_TNEFERR(problemarray) \
  377. { \
  378. if (problemarray) \
  379. { \
  380. const std::wstring szProbArray = error::TnefProblemArrayToString(*(problemarray)); \
  381. error::ErrDialog(__FILE__, __LINE__, IDS_EDTNEFPROBLEMARRAY, szProbArray.c_str()); \
  382. output::DebugPrint(output::dbgLevel::Generic, L"TNEF Problem array:\n%ws\n", szProbArray.c_str()); \
  383. } \
  384. }