diff --git a/rehlds/HLTV/Console/build.gradle b/rehlds/HLTV/Console/build.gradle index 5649284..7d07df5 100644 --- a/rehlds/HLTV/Console/build.gradle +++ b/rehlds/HLTV/Console/build.gradle @@ -116,6 +116,7 @@ model { include "ObjectList.cpp" include "TokenLine.cpp" include "textconsole.cpp" + include "minidump.cpp" if (GradleCppUtils.windows) { include "TextConsoleWin32.cpp" } diff --git a/rehlds/HLTV/Console/msvc/Console.vcxproj b/rehlds/HLTV/Console/msvc/Console.vcxproj index 380c0e1..cdeb0e3 100644 --- a/rehlds/HLTV/Console/msvc/Console.vcxproj +++ b/rehlds/HLTV/Console/msvc/Console.vcxproj @@ -19,6 +19,7 @@ + @@ -60,6 +61,11 @@ Use Use + + Use + Use + Use + Use @@ -252,4 +258,4 @@ - \ No newline at end of file + diff --git a/rehlds/HLTV/Console/msvc/Console.vcxproj.filters b/rehlds/HLTV/Console/msvc/Console.vcxproj.filters index 0bc41ab..715f2ca 100644 --- a/rehlds/HLTV/Console/msvc/Console.vcxproj.filters +++ b/rehlds/HLTV/Console/msvc/Console.vcxproj.filters @@ -58,6 +58,9 @@ engine + + common + @@ -96,6 +99,9 @@ engine + + common + diff --git a/rehlds/HLTV/Console/src/System.cpp b/rehlds/HLTV/Console/src/System.cpp index 9f70cdc..54db254 100644 --- a/rehlds/HLTV/Console/src/System.cpp +++ b/rehlds/HLTV/Console/src/System.cpp @@ -1092,8 +1092,15 @@ unsigned char *System::LoadFile(const char *name, int *length) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { - gSystem.BuildCommandLine(lpCmdLine); - return gSystem.Run(); +#ifdef HLTV_FIXES + return CatchAndWriteMiniDump([=]() + { +#endif + gSystem.BuildCommandLine(lpCmdLine); + return gSystem.Run(); +#ifdef HLTV_FIXES + }, lpCmdLine); +#endif } void System::BuildCommandLine(char *argv) diff --git a/rehlds/HLTV/Console/src/precompiled.h b/rehlds/HLTV/Console/src/precompiled.h index 828c1ff..438bae6 100644 --- a/rehlds/HLTV/Console/src/precompiled.h +++ b/rehlds/HLTV/Console/src/precompiled.h @@ -1,5 +1,7 @@ #pragma once +#include "version/appversion.h" + #include "basetypes.h" #include "FileSystem.h" #include "strtools.h" @@ -16,3 +18,7 @@ // Console stuff #include "System.h" #include "common/random.h" + +#ifdef _WIN32 +#include "minidump.h" +#endif diff --git a/rehlds/common/minidump.cpp b/rehlds/common/minidump.cpp new file mode 100644 index 0000000..f1ee969 --- /dev/null +++ b/rehlds/common/minidump.cpp @@ -0,0 +1,203 @@ +#include "precompiled.h" + +#if defined(HLTV_FIXES) || defined(LAUNCHER_FIXES) +#ifdef _WIN32 + +#if defined(_MSC_VER) && !defined(_IMAGEHLP_) +#pragma warning(push) +#pragma warning(disable:4091) // A microsoft header has warnings. Very nice. +#include +#pragma warning(pop) +#endif + +// Gets last error. +static inline HRESULT GetLastHresult() +{ + return HRESULT_FROM_WIN32(GetLastError()); +} + +#define WIDE_TEXT_HELPER_(quote) L##quote +#define WIDE_TEXT(quote) WIDE_TEXT_HELPER_(quote) + +// Gets application version which is safe for use in minidump file name. Can be entirely constexpr with C++17 and std::array. +template +static wchar_t* GetAppVersionForMiniDumpName(wchar_t (&appVersion)[appVersionSize]) { + constexpr wchar_t rawAppVersion[]{WIDE_TEXT(APP_VERSION "-" APP_COMMIT_SHA)}; + + static_assert(appVersionSize >= ARRAYSIZE(rawAppVersion), "App version buffer size should be enough to store app version."); + + // See https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-pathcleanupspec#remarks for details. + constexpr wchar_t invalidPathChars[]{L'\\', L'/', L':', L'*', L'?', L'"', L'<', L'>', L'|', L';', L','}; + + size_t outIt{0}; + for (size_t rawIt{0}; rawIt < ARRAYSIZE(rawAppVersion); ++rawIt) + { + const wchar_t currentRawIt{rawAppVersion[rawIt]}; + bool isValidRaw{true}; + + for (size_t invalidIt{0}; invalidIt < ARRAYSIZE(invalidPathChars); ++invalidIt) + { + isValidRaw = invalidPathChars[invalidIt] != currentRawIt; + + if (!isValidRaw) + { + break; + } + } + + if (isValidRaw) { + appVersion[outIt++] = currentRawIt; + } + } + + return appVersion; +} + +// Creates a new minidump file and dumps the exception info into it. +static HRESULT WriteMiniDumpUsingExceptionInfo(unsigned int exceptionCode, + struct _EXCEPTION_POINTERS *exceptionInfo, MINIDUMP_TYPE minidumpType) +{ + // Counter used to make sure minidump names are unique. + static int minidumpsWrittenCount{0}; + + HMODULE dbghelpModule{LoadLibraryW(L"DbgHelp.dll")}; + HRESULT errorCode{dbghelpModule ? S_OK : GetLastHresult()}; + + using MiniDumpWriteDumpFn = decltype(&MiniDumpWriteDump); + MiniDumpWriteDumpFn miniDumpWriteDump{nullptr}; + if (SUCCEEDED(errorCode)) + { + miniDumpWriteDump = reinterpret_cast(GetProcAddress( + dbghelpModule, "MiniDumpWriteDump")); + errorCode = miniDumpWriteDump ? S_OK : GetLastHresult(); + } + + // Creates a unique filename for the minidump based on the current time and + // module name. + const time_t timeNow{time(nullptr)}; + tm *localTimeNow{localtime(&timeNow)}; + if (localTimeNow == nullptr) + { + errorCode = E_INVALIDARG; + } + + // Strip off the rest of the path from the .exe name. + wchar_t moduleName[MAX_PATH]; + if (SUCCEEDED(errorCode)) + { + ::SetLastError(NOERROR); + ::GetModuleFileNameW(nullptr, moduleName, static_cast(ARRAYSIZE(moduleName))); + + errorCode = GetLastHresult(); + } + + wchar_t fileName[MAX_PATH]; + HANDLE minidumpFile{nullptr}; + + if (SUCCEEDED(errorCode)) + { + wchar_t *strippedModuleName{wcsrchr(moduleName, L'.')}; + if (strippedModuleName) *strippedModuleName = L'\0'; + + strippedModuleName = wcsrchr(moduleName, L'\\'); + // Move past the last slash. + if (strippedModuleName) ++strippedModuleName; + + wchar_t appVersion[ARRAYSIZE(APP_VERSION) + ARRAYSIZE(APP_COMMIT_SHA) + 2]; + // __crash__