display stack trace on exceptions
This commit is contained in:
		
							parent
							
								
									050b1a504e
								
							
						
					
					
						commit
						0eceb235ca
					
				|  | @ -94,6 +94,7 @@ | ||||||
|     <ClInclude Include="third-party\ImGui\imstb_rectpack.h" /> |     <ClInclude Include="third-party\ImGui\imstb_rectpack.h" /> | ||||||
|     <ClInclude Include="third-party\ImGui\imstb_textedit.h" /> |     <ClInclude Include="third-party\ImGui\imstb_textedit.h" /> | ||||||
|     <ClInclude Include="third-party\ImGui\imstb_truetype.h" /> |     <ClInclude Include="third-party\ImGui\imstb_truetype.h" /> | ||||||
|  |     <ClInclude Include="third-party\StackWalker\StackWalker.h" /> | ||||||
|     <ClInclude Include="ui\Button.h" /> |     <ClInclude Include="ui\Button.h" /> | ||||||
|     <ClInclude Include="ui\Menu.h" /> |     <ClInclude Include="ui\Menu.h" /> | ||||||
|     <ClInclude Include="utils\DataUtil.h" /> |     <ClInclude Include="utils\DataUtil.h" /> | ||||||
|  | @ -153,6 +154,7 @@ | ||||||
|     <ClCompile Include="third-party\ImGui\imgui_demo.cpp" /> |     <ClCompile Include="third-party\ImGui\imgui_demo.cpp" /> | ||||||
|     <ClCompile Include="third-party\ImGui\imgui_draw.cpp" /> |     <ClCompile Include="third-party\ImGui\imgui_draw.cpp" /> | ||||||
|     <ClCompile Include="third-party\ImGui\imgui_widgets.cpp" /> |     <ClCompile Include="third-party\ImGui\imgui_widgets.cpp" /> | ||||||
|  |     <ClCompile Include="third-party\StackWalker\StackWalker.cpp" /> | ||||||
|     <ClCompile Include="ui\Button.cpp" /> |     <ClCompile Include="ui\Button.cpp" /> | ||||||
|     <ClCompile Include="ui\Menu.cpp" /> |     <ClCompile Include="ui\Menu.cpp" /> | ||||||
|     <ClCompile Include="utils\DataUtil.cpp" /> |     <ClCompile Include="utils\DataUtil.cpp" /> | ||||||
|  |  | ||||||
|  | @ -40,6 +40,9 @@ | ||||||
|     <Filter Include="third-party\ImGui"> |     <Filter Include="third-party\ImGui"> | ||||||
|       <UniqueIdentifier>{2f24af2d-eba6-4be1-9ac4-fc58aeac6670}</UniqueIdentifier> |       <UniqueIdentifier>{2f24af2d-eba6-4be1-9ac4-fc58aeac6670}</UniqueIdentifier> | ||||||
|     </Filter> |     </Filter> | ||||||
|  |     <Filter Include="third-party\StackWalker"> | ||||||
|  |       <UniqueIdentifier>{1fec4835-63a1-4612-80b5-828dadf0ac63}</UniqueIdentifier> | ||||||
|  |     </Filter> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="ui\Button.h"> |     <ClInclude Include="ui\Button.h"> | ||||||
|  | @ -327,6 +330,9 @@ | ||||||
|     <ClInclude Include="imgui\imgui_impl.hpp"> |     <ClInclude Include="imgui\imgui_impl.hpp"> | ||||||
|       <Filter>imgui</Filter> |       <Filter>imgui</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="third-party\StackWalker\StackWalker.h"> | ||||||
|  |       <Filter>third-party\StackWalker</Filter> | ||||||
|  |     </ClInclude> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="ui\Button.cpp"> |     <ClCompile Include="ui\Button.cpp"> | ||||||
|  | @ -500,5 +506,8 @@ | ||||||
|     <ClCompile Include="imgui\imgui_impl_dx10.cpp"> |     <ClCompile Include="imgui\imgui_impl_dx10.cpp"> | ||||||
|       <Filter>imgui</Filter> |       <Filter>imgui</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="third-party\StackWalker\StackWalker.cpp"> | ||||||
|  |       <Filter>third-party\StackWalker</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| </Project> | </Project> | ||||||
|  | @ -70,7 +70,6 @@ namespace kiwano | ||||||
| 
 | 
 | ||||||
| 	Logger::Logger() | 	Logger::Logger() | ||||||
| 		: enabled_(true) | 		: enabled_(true) | ||||||
| 		, has_console_(false) |  | ||||||
| 		, default_stdout_color_(0) | 		, default_stdout_color_(0) | ||||||
| 		, default_stderr_color_(0) | 		, default_stderr_color_(0) | ||||||
| 		, output_stream_(std::wcout.rdbuf()) | 		, output_stream_(std::wcout.rdbuf()) | ||||||
|  | @ -81,8 +80,8 @@ namespace kiwano | ||||||
| 
 | 
 | ||||||
| 	void Logger::ResetOutputStream() | 	void Logger::ResetOutputStream() | ||||||
| 	{ | 	{ | ||||||
| 		has_console_ = ::GetConsoleWindow() != nullptr; | 		bool has_console = ::GetConsoleWindow() != nullptr; | ||||||
| 		if (has_console_) | 		if (has_console) | ||||||
| 		{ | 		{ | ||||||
| 			default_stdout_color_ = default_stderr_color_ = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; | 			default_stdout_color_ = default_stderr_color_ = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; | ||||||
| 
 | 
 | ||||||
|  | @ -161,7 +160,7 @@ namespace kiwano | ||||||
| 
 | 
 | ||||||
| 	void Logger::Outputf(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, const wchar_t* format, va_list args) const | 	void Logger::Outputf(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, const wchar_t* format, va_list args) const | ||||||
| 	{ | 	{ | ||||||
| 		if (enabled_ && has_console_) | 		if (enabled_) | ||||||
| 		{ | 		{ | ||||||
| 			std::wstring output = MakeOutputStringf(prompt, format, args); | 			std::wstring output = MakeOutputStringf(prompt, format, args); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -115,7 +115,6 @@ namespace kiwano | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		bool enabled_; | 		bool enabled_; | ||||||
| 		bool has_console_; |  | ||||||
| 		WORD default_stdout_color_; | 		WORD default_stdout_color_; | ||||||
| 		WORD default_stderr_color_; | 		WORD default_stderr_color_; | ||||||
| 
 | 
 | ||||||
|  | @ -223,7 +222,7 @@ namespace kiwano | ||||||
| 	template <typename ..._Args> | 	template <typename ..._Args> | ||||||
| 	void Logger::OutputLine(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const | 	void Logger::OutputLine(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const | ||||||
| 	{ | 	{ | ||||||
| 		if (enabled_ && has_console_) | 		if (enabled_) | ||||||
| 		{ | 		{ | ||||||
| 			Output(os, color, prompt, std::forward<_Args>(args)...); | 			Output(os, color, prompt, std::forward<_Args>(args)...); | ||||||
| 
 | 
 | ||||||
|  | @ -235,7 +234,7 @@ namespace kiwano | ||||||
| 	template <typename ..._Args> | 	template <typename ..._Args> | ||||||
| 	void Logger::Output(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const | 	void Logger::Output(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const | ||||||
| 	{ | 	{ | ||||||
| 		if (enabled_ && has_console_) | 		if (enabled_) | ||||||
| 		{ | 		{ | ||||||
| 			std::wstring output = MakeOutputString(prompt, std::forward<_Args>(args)...); | 			std::wstring output = MakeOutputString(prompt, std::forward<_Args>(args)...); | ||||||
| 
 | 
 | ||||||
|  | @ -273,6 +272,12 @@ namespace kiwano | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | //
 | ||||||
|  | // Display stack trace on exception
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | #include "../third-party/StackWalker/StackWalker.h" | ||||||
|  | 
 | ||||||
| namespace kiwano | namespace kiwano | ||||||
| { | { | ||||||
| 	inline void ThrowIfFailed(HRESULT hr) | 	inline void ThrowIfFailed(HRESULT hr) | ||||||
|  | @ -281,6 +286,8 @@ namespace kiwano | ||||||
| 		{ | 		{ | ||||||
| 			KGE_ERROR_LOG(L"Fatal error with HRESULT of %08X", hr); | 			KGE_ERROR_LOG(L"Fatal error with HRESULT of %08X", hr); | ||||||
| 
 | 
 | ||||||
|  | 			StackWalker{}.ShowCallstack(); | ||||||
|  | 
 | ||||||
| 			static char buffer[1024 + 1]; | 			static char buffer[1024 + 1]; | ||||||
| 			sprintf_s(buffer, "Fatal error with HRESULT of %08X", hr); | 			sprintf_s(buffer, "Fatal error with HRESULT of %08X", hr); | ||||||
| 			throw std::runtime_error(buffer); | 			throw std::runtime_error(buffer); | ||||||
|  |  | ||||||
|  | @ -45,45 +45,34 @@ namespace kiwano | ||||||
| 	{ | 	{ | ||||||
| 		KGE_LOG(L"Creating device resources"); | 		KGE_LOG(L"Creating device resources"); | ||||||
| 
 | 
 | ||||||
| 		HRESULT hr; |  | ||||||
| 
 |  | ||||||
| 		hwnd_ = app->GetWindow()->GetHandle(); | 		hwnd_ = app->GetWindow()->GetHandle(); | ||||||
| 		hr = hwnd_ ? S_OK : E_FAIL; | 
 | ||||||
|  | 		ThrowIfFailed(hwnd_ ? S_OK : E_FAIL); | ||||||
| 		 | 		 | ||||||
| 		if (SUCCEEDED(hr)) | 		device_resources_ = nullptr; | ||||||
| 		{ | 		drawing_state_block_ = nullptr; | ||||||
| 			device_resources_ = nullptr; | 
 | ||||||
| 			hr = DeviceResources::Create( | 		ThrowIfFailed( | ||||||
|  | 			DeviceResources::Create( | ||||||
| 				&device_resources_, | 				&device_resources_, | ||||||
| 				hwnd_ | 				hwnd_ | ||||||
| 			); | 			) | ||||||
| 		} | 		); | ||||||
| 
 | 
 | ||||||
| 		if (SUCCEEDED(hr)) | 		factory_ = device_resources_->GetD2DFactory(); | ||||||
| 		{ | 		device_context_ = device_resources_->GetD2DDeviceContext(); | ||||||
| 			factory_ = device_resources_->GetD2DFactory(); |  | ||||||
| 			device_context_ = device_resources_->GetD2DDeviceContext(); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		if (SUCCEEDED(hr)) | 		ThrowIfFailed( | ||||||
| 		{ | 			factory_->CreateDrawingStateBlock( | ||||||
| 			drawing_state_block_ = nullptr; |  | ||||||
| 			hr = factory_->CreateDrawingStateBlock( |  | ||||||
| 				&drawing_state_block_ | 				&drawing_state_block_ | ||||||
| 			); | 			) | ||||||
| 		} | 		); | ||||||
| 
 | 
 | ||||||
| 		if (SUCCEEDED(hr)) | 		ThrowIfFailed( | ||||||
| 		{ | 			CreateDeviceResources() | ||||||
| 			hr = CreateDeviceResources(); | 		); | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		if (SUCCEEDED(hr)) | 		output_size_ = app->GetWindow()->GetSize(); | ||||||
| 		{ |  | ||||||
| 			output_size_ = app->GetWindow()->GetSize(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		ThrowIfFailed(hr); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void Renderer::DestroyComponent() | 	void Renderer::DestroyComponent() | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,189 @@ | ||||||
|  | /**********************************************************************
 | ||||||
|  |  *  | ||||||
|  |  * StackWalker.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * History: | ||||||
|  |  *  2005-07-27   v1    - First public release on http://www.codeproject.com/
 | ||||||
|  |  *  (for additional changes see History in 'StackWalker.cpp'! | ||||||
|  |  * | ||||||
|  |  **********************************************************************/ | ||||||
|  | // #pragma once is supported starting with _MCS_VER 1000, 
 | ||||||
|  | // so we need not to check the version (because we only support _MSC_VER >= 1100)!
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <windows.h> | ||||||
|  | 
 | ||||||
|  | // special defines for VC5/6 (if no actual PSDK is installed):
 | ||||||
|  | #if _MSC_VER < 1300 | ||||||
|  | typedef unsigned __int64 DWORD64, *PDWORD64; | ||||||
|  | #if defined(_WIN64) | ||||||
|  | typedef unsigned __int64 SIZE_T, *PSIZE_T; | ||||||
|  | #else | ||||||
|  | typedef unsigned long SIZE_T, *PSIZE_T; | ||||||
|  | #endif | ||||||
|  | #endif  // _MSC_VER < 1300
 | ||||||
|  | 
 | ||||||
|  | class StackWalkerInternal;  // forward
 | ||||||
|  | class StackWalker | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |   typedef enum StackWalkOptions | ||||||
|  |   { | ||||||
|  |     // No addition info will be retrived 
 | ||||||
|  |     // (only the address is available)
 | ||||||
|  |     RetrieveNone = 0, | ||||||
|  |      | ||||||
|  |     // Try to get the symbol-name
 | ||||||
|  |     RetrieveSymbol = 1, | ||||||
|  |      | ||||||
|  |     // Try to get the line for this symbol
 | ||||||
|  |     RetrieveLine = 2, | ||||||
|  |      | ||||||
|  |     // Try to retrieve the module-infos
 | ||||||
|  |     RetrieveModuleInfo = 4, | ||||||
|  |      | ||||||
|  |     // Also retrieve the version for the DLL/EXE
 | ||||||
|  |     RetrieveFileVersion = 8, | ||||||
|  |      | ||||||
|  |     // Contains all the abouve
 | ||||||
|  |     RetrieveVerbose = 0xF, | ||||||
|  |      | ||||||
|  |     // Generate a "good" symbol-search-path
 | ||||||
|  |     SymBuildPath = 0x10, | ||||||
|  |      | ||||||
|  |     // Also use the public Microsoft-Symbol-Server
 | ||||||
|  |     SymUseSymSrv = 0x20, | ||||||
|  |      | ||||||
|  |     // Contains all the abouve "Sym"-options
 | ||||||
|  |     SymAll = 0x30, | ||||||
|  |      | ||||||
|  |     // Contains all options (default)
 | ||||||
|  |     OptionsAll = 0x3F | ||||||
|  |   } StackWalkOptions; | ||||||
|  | 
 | ||||||
|  |   StackWalker( | ||||||
|  |     int options = OptionsAll, // 'int' is by design, to combine the enum-flags
 | ||||||
|  |     LPCSTR szSymPath = NULL,  | ||||||
|  |     DWORD dwProcessId = GetCurrentProcessId(),  | ||||||
|  |     HANDLE hProcess = GetCurrentProcess() | ||||||
|  |     ); | ||||||
|  |   StackWalker(DWORD dwProcessId, HANDLE hProcess); | ||||||
|  |   virtual ~StackWalker(); | ||||||
|  | 
 | ||||||
|  |   typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( | ||||||
|  |     HANDLE      hProcess, | ||||||
|  |     DWORD64     qwBaseAddress, | ||||||
|  |     PVOID       lpBuffer, | ||||||
|  |     DWORD       nSize, | ||||||
|  |     LPDWORD     lpNumberOfBytesRead, | ||||||
|  |     LPVOID      pUserData  // optional data, which was passed in "ShowCallstack"
 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |   BOOL LoadModules(); | ||||||
|  | 
 | ||||||
|  |   BOOL ShowCallstack( | ||||||
|  |     HANDLE hThread = GetCurrentThread(),  | ||||||
|  |     const CONTEXT *context = NULL,  | ||||||
|  |     PReadProcessMemoryRoutine readMemoryFunction = NULL, | ||||||
|  |     LPVOID pUserData = NULL  // optional to identify some data in the 'readMemoryFunction'-callback
 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | #if _MSC_VER >= 1300 | ||||||
|  | // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" 
 | ||||||
|  | // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
 | ||||||
|  | protected: | ||||||
|  | #endif | ||||||
|  | 	enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
 | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |   // Entry for each Callstack-Entry
 | ||||||
|  |   typedef struct CallstackEntry | ||||||
|  |   { | ||||||
|  |     DWORD64 offset;  // if 0, we have no valid entry
 | ||||||
|  |     CHAR name[STACKWALK_MAX_NAMELEN]; | ||||||
|  |     CHAR undName[STACKWALK_MAX_NAMELEN]; | ||||||
|  |     CHAR undFullName[STACKWALK_MAX_NAMELEN]; | ||||||
|  |     DWORD64 offsetFromSmybol; | ||||||
|  |     DWORD offsetFromLine; | ||||||
|  |     DWORD lineNumber; | ||||||
|  |     CHAR lineFileName[STACKWALK_MAX_NAMELEN]; | ||||||
|  |     DWORD symType; | ||||||
|  |     LPCSTR symTypeString; | ||||||
|  |     CHAR moduleName[STACKWALK_MAX_NAMELEN]; | ||||||
|  |     DWORD64 baseOfImage; | ||||||
|  |     CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; | ||||||
|  |   } CallstackEntry; | ||||||
|  | 
 | ||||||
|  |   enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; | ||||||
|  | 
 | ||||||
|  |   virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); | ||||||
|  |   virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); | ||||||
|  |   virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); | ||||||
|  |   virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); | ||||||
|  |   virtual void OnOutput(LPCSTR szText); | ||||||
|  | 
 | ||||||
|  |   StackWalkerInternal *m_sw; | ||||||
|  |   HANDLE m_hProcess; | ||||||
|  |   DWORD m_dwProcessId; | ||||||
|  |   BOOL m_modulesLoaded; | ||||||
|  |   LPSTR m_szSymPath; | ||||||
|  | 
 | ||||||
|  |   int m_options; | ||||||
|  | 
 | ||||||
|  |   static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); | ||||||
|  | 
 | ||||||
|  |   friend StackWalkerInternal; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // The "ugly" assembler-implementation is needed for systems before XP
 | ||||||
|  | // If you have a new PSDK and you only compile for XP and later, then you can use 
 | ||||||
|  | // the "RtlCaptureContext"
 | ||||||
|  | // Currently there is no define which determines the PSDK-Version... 
 | ||||||
|  | // So we just use the compiler-version (and assumes that the PSDK is 
 | ||||||
|  | // the one which was installed by the VS-IDE)
 | ||||||
|  | 
 | ||||||
|  | // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
 | ||||||
|  | //       But I currently use it in x64/IA64 environments...
 | ||||||
|  | //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
 | ||||||
|  | 
 | ||||||
|  | #if defined(_M_IX86) | ||||||
|  | #ifdef CURRENT_THREAD_VIA_EXCEPTION | ||||||
|  | // TODO: The following is not a "good" implementation, 
 | ||||||
|  | // because the callstack is only valid in the "__except" block...
 | ||||||
|  | #define GET_CURRENT_CONTEXT(c, contextFlags) \ | ||||||
|  |   do { \ | ||||||
|  |     memset(&c, 0, sizeof(CONTEXT)); \ | ||||||
|  |     EXCEPTION_POINTERS *pExp = NULL; \ | ||||||
|  |     __try { \ | ||||||
|  |       throw 0; \ | ||||||
|  |     } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ | ||||||
|  |     if (pExp != NULL) \ | ||||||
|  |       memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ | ||||||
|  |       c.ContextFlags = contextFlags; \ | ||||||
|  |   } while(0); | ||||||
|  | #else | ||||||
|  | // The following should be enough for walking the callstack...
 | ||||||
|  | #define GET_CURRENT_CONTEXT(c, contextFlags) \ | ||||||
|  |   do { \ | ||||||
|  |     memset(&c, 0, sizeof(CONTEXT)); \ | ||||||
|  |     c.ContextFlags = contextFlags; \ | ||||||
|  |     __asm    call x \ | ||||||
|  |     __asm x: pop eax \ | ||||||
|  |     __asm    mov c.Eip, eax \ | ||||||
|  |     __asm    mov c.Ebp, ebp \ | ||||||
|  |     __asm    mov c.Esp, esp \ | ||||||
|  |   } while(0); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | // The following is defined for x86 (XP and higher), x64 and IA64:
 | ||||||
|  | #define GET_CURRENT_CONTEXT(c, contextFlags) \ | ||||||
|  |   do { \ | ||||||
|  |     memset(&c, 0, sizeof(CONTEXT)); \ | ||||||
|  |     c.ContextFlags = contextFlags; \ | ||||||
|  |     RtlCaptureContext(&c); \ | ||||||
|  | } while(0); | ||||||
|  | #endif | ||||||
		Loading…
	
		Reference in New Issue