feat: add windows function helper
- add windows function helper namespace for some commonly used windows functions. - add corresponding testbench for added code.
This commit is contained in:
parent
ab12268395
commit
015ff874f8
|
@ -12,6 +12,7 @@ PRIVATE
|
||||||
${CMAKE_CURRENT_LIST_DIR}/IOHelper.hpp
|
${CMAKE_CURRENT_LIST_DIR}/IOHelper.hpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ParserHelper.hpp
|
${CMAKE_CURRENT_LIST_DIR}/ParserHelper.hpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/StringHelper.hpp
|
${CMAKE_CURRENT_LIST_DIR}/StringHelper.hpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/WinFctHelper.hpp
|
||||||
# Windows including guard pair
|
# Windows including guard pair
|
||||||
${CMAKE_CURRENT_LIST_DIR}/WinImportPrefix.hpp
|
${CMAKE_CURRENT_LIST_DIR}/WinImportPrefix.hpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/WinImportSuffix.hpp
|
${CMAKE_CURRENT_LIST_DIR}/WinImportSuffix.hpp
|
||||||
|
@ -27,6 +28,7 @@ PRIVATE
|
||||||
${CMAKE_CURRENT_LIST_DIR}/IOHelper.cpp
|
${CMAKE_CURRENT_LIST_DIR}/IOHelper.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ParserHelper.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ParserHelper.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/StringHelper.cpp
|
${CMAKE_CURRENT_LIST_DIR}/StringHelper.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/WinFctHelper.cpp
|
||||||
)
|
)
|
||||||
# Setup header infomations
|
# Setup header infomations
|
||||||
target_include_directories(YYCCommonplace
|
target_include_directories(YYCCommonplace
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include "WinFctHelper.hpp"
|
||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
#include "WinImportPrefix.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
@ -14,33 +15,40 @@
|
||||||
namespace YYCC::ExceptionHelper {
|
namespace YYCC::ExceptionHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief true if the exception handler already registered.
|
* @brief True if the exception handler already registered, otherwise false.
|
||||||
* This variable is served for singleton.
|
* @details
|
||||||
|
* This variable is designed to prevent multiple register operation
|
||||||
|
* because unhandled exception handler should only be registered once.
|
||||||
|
* \n
|
||||||
|
* Register function should check whether this variable is false before registering,
|
||||||
|
* and set this variable to true after registing.
|
||||||
|
* Unregister as well as should do the same check.
|
||||||
*/
|
*/
|
||||||
static bool g_IsRegistered = false;
|
static bool g_IsRegistered = false;
|
||||||
/**
|
/**
|
||||||
* @brief true if a exception handler is running.
|
* @brief True if a exception handler is running, otherwise false.
|
||||||
|
* @details
|
||||||
* This variable is served for blocking possible infinity recursive exception handling.
|
* This variable is served for blocking possible infinity recursive exception handling.
|
||||||
|
* \n
|
||||||
|
* When entering unhandled exception handler, we must check whether this variable is true.
|
||||||
|
* If it is true, it mean that there is another unhandled exception handler running.
|
||||||
|
* Then we should exit immediately.
|
||||||
|
* Otherwise, this variable should be set to true indicating we are processing unhandled exception.
|
||||||
|
* After processing exception, at the end of unhandled exception handler,
|
||||||
|
* we should restore this value to false.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
static bool g_IsProcessing = false;
|
static bool g_IsProcessing = false;
|
||||||
/**
|
/**
|
||||||
* @brief The backup of original exception handler.
|
* @brief The backup of original exception handler.
|
||||||
|
* @details
|
||||||
|
* This variable was set when registering unhandled exception handler.
|
||||||
|
* And will be used when unregistering for restoring.
|
||||||
*/
|
*/
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
static LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
||||||
|
|
||||||
#pragma region Exception Handler Implementation
|
#pragma region Exception Handler Implementation
|
||||||
|
|
||||||
static HMODULE GetCurrentModule() {
|
|
||||||
// Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
|
|
||||||
HMODULE hModule = NULL;
|
|
||||||
GetModuleHandleExW(
|
|
||||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, // get address and do not inc ref counter.
|
|
||||||
(LPCWSTR)GetCurrentModule,
|
|
||||||
&hModule);
|
|
||||||
|
|
||||||
return hModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* UExceptionGetCodeName(DWORD code) {
|
static const char* UExceptionGetCodeName(DWORD code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case EXCEPTION_ACCESS_VIOLATION:
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
|
@ -241,7 +249,7 @@ namespace YYCC::ExceptionHelper {
|
||||||
std::filesystem::path ironpad_path;
|
std::filesystem::path ironpad_path;
|
||||||
WCHAR module_path[MAX_PATH];
|
WCHAR module_path[MAX_PATH];
|
||||||
std::memset(module_path, 0, sizeof(module_path));
|
std::memset(module_path, 0, sizeof(module_path));
|
||||||
if (GetModuleFileNameW(GetCurrentModule(), module_path, MAX_PATH) == 0) {
|
if (GetModuleFileNameW(WinFctHelper::GetCurrentModule(), module_path, MAX_PATH) == 0) {
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
ironpad_path = module_path;
|
ironpad_path = module_path;
|
||||||
|
|
|
@ -6,12 +6,28 @@ namespace YYCC::ExceptionHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register unhandled exception handler
|
* @brief Register unhandled exception handler
|
||||||
* @detail This function frequently called at the start of program.
|
* @details
|
||||||
|
* This function will set an internal function as unhandled exception handler on Windows.
|
||||||
|
* \n
|
||||||
|
* When unhandled exception raised,
|
||||||
|
* That internal function will output error stacktrace in standard output
|
||||||
|
* and log file (located in temp folder), and also generate a dump file
|
||||||
|
* in temp folder (for convenient debugging of developer when reporting bugs) if it can.
|
||||||
|
* \n
|
||||||
|
* This function usually is called at the start of program.
|
||||||
|
* @remarks This function is Windows only.
|
||||||
*/
|
*/
|
||||||
void Register();
|
void Register();
|
||||||
/**
|
/**
|
||||||
* @brief Unregiister unhandled exception handler
|
* @brief Unregister unhandled exception handler
|
||||||
* @detail This function frequently called at the end of program.
|
* @details
|
||||||
|
* The reverse operation of Register().
|
||||||
|
* \n
|
||||||
|
* This function and Register() should always be used as a pair.
|
||||||
|
* You must call this function if you have called Register() before.
|
||||||
|
* \n
|
||||||
|
* This function usually is called at the end of program.
|
||||||
|
* @remarks This function is Windows only.
|
||||||
*/
|
*/
|
||||||
void Unregister();
|
void Unregister();
|
||||||
|
|
||||||
|
|
78
src/WinFctHelper.cpp
Normal file
78
src/WinFctHelper.cpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#include "WinFctHelper.hpp"
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
#include "EncodingHelper.hpp"
|
||||||
|
|
||||||
|
namespace YYCC::WinFctHelper {
|
||||||
|
|
||||||
|
HMODULE GetCurrentModule() {
|
||||||
|
// Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
|
||||||
|
HMODULE hModule = NULL;
|
||||||
|
GetModuleHandleExW(
|
||||||
|
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, // get address and do not inc ref counter.
|
||||||
|
(LPCWSTR)GetCurrentModule,
|
||||||
|
&hModule);
|
||||||
|
|
||||||
|
return hModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetTempDirectory() {
|
||||||
|
// create wchar buffer for receiving the temp path.
|
||||||
|
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
||||||
|
DWORD expected_size;
|
||||||
|
|
||||||
|
// fetch temp folder
|
||||||
|
while (true) {
|
||||||
|
if ((expected_size = GetTempPathW(static_cast<DWORD>(wpath.size()), wpath.data())) == 0) {
|
||||||
|
// failed, set to empty
|
||||||
|
expected_size = 0;
|
||||||
|
// and break while
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected_size > static_cast<DWORD>(wpath.size())) {
|
||||||
|
// buffer is too short, need enlarge and do fetching again
|
||||||
|
wpath.resize(expected_size);
|
||||||
|
} else {
|
||||||
|
// ok. shrink to real length, break while
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize result
|
||||||
|
wpath.resize(expected_size);
|
||||||
|
// convert to utf8 and return
|
||||||
|
return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetModuleName(HINSTANCE hModule) {
|
||||||
|
// create wchar buffer for receiving the temp path.
|
||||||
|
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
||||||
|
DWORD copied_size;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if ((copied_size = GetModuleFileNameW(hModule, wpath.data(), static_cast<DWORD>(wpath.size()))) == 0) {
|
||||||
|
// failed, return empty string
|
||||||
|
copied_size = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check insufficient buffer
|
||||||
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
// buffer is not enough, enlarge it and try again.
|
||||||
|
wpath.resize(wpath.size() + MAX_PATH);
|
||||||
|
} else {
|
||||||
|
// ok, break while
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize result
|
||||||
|
wpath.resize(copied_size);
|
||||||
|
// convert to utf8 and return
|
||||||
|
return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
43
src/WinFctHelper.hpp
Normal file
43
src/WinFctHelper.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
#include "YYCCInternal.hpp"
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "WinImportPrefix.hpp"
|
||||||
|
#include <Windows.h>
|
||||||
|
#include "WinImportSuffix.hpp"
|
||||||
|
|
||||||
|
namespace YYCC::WinFctHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get Windows used HANDLE for current module.
|
||||||
|
* @details
|
||||||
|
* If your target is EXE, the current module simply is your program self.
|
||||||
|
* However, if your target is DLL, the current module is your DLL, not the EXE loading your DLL.
|
||||||
|
* \n
|
||||||
|
* This function is frequently used by DLL.
|
||||||
|
* Because some design need the HANDLE of current module, not the host EXE loading your DLL.
|
||||||
|
* For example, you may want to get the name of your built DLL at runtime, then you should pass current module HANDLE, not the HANDLE of EXE.
|
||||||
|
* Or, if you want to get the path to your DLL, you also should pass current module HANDLE.
|
||||||
|
* @return A Windows HANDLE pointing to current module, NULL if failed.
|
||||||
|
*/
|
||||||
|
HMODULE GetCurrentModule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get path to Windows temp folder.
|
||||||
|
* @return UTF8 encoded path to Windows temp folder. Empty string if failed.
|
||||||
|
*/
|
||||||
|
std::string GetTempDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the file name of given module HANDLE
|
||||||
|
* @param hModule[in]
|
||||||
|
* The HANDLE to the module where we want get file name.
|
||||||
|
* It is same as the HANDLE parameter of GetModuleFileName.
|
||||||
|
* @return UTF8 encoded file name of given module. Empty string if failed.
|
||||||
|
*/
|
||||||
|
std::string GetModuleName(HINSTANCE hModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,3 +7,6 @@
|
||||||
#include "ConsoleHelper.hpp"
|
#include "ConsoleHelper.hpp"
|
||||||
#include "DialogHelper.hpp"
|
#include "DialogHelper.hpp"
|
||||||
#include "ParserHelper.hpp"
|
#include "ParserHelper.hpp"
|
||||||
|
#include "ExceptionHelper.hpp"
|
||||||
|
#include "IOHelper.hpp"
|
||||||
|
#include "WinFctHelper.hpp"
|
||||||
|
|
|
@ -215,11 +215,18 @@ namespace YYCCTestbench {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void WinFctTestbench() {
|
||||||
|
Console::WriteLine("Current Module HANDLE: 0x%016" PRIXPTR, YYCC::WinFctHelper::GetCurrentModule());
|
||||||
|
Console::WriteLine("Temp Directory: %s", YYCC::WinFctHelper::GetTempDirectory().c_str());
|
||||||
|
Console::WriteLine("Current Module Name: %s", YYCC::WinFctHelper::GetModuleName(YYCC::WinFctHelper::GetCurrentModule()).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** args) {
|
int main(int argc, char** args) {
|
||||||
YYCCTestbench::ConsoleTestbench();
|
//YYCCTestbench::ConsoleTestbench();
|
||||||
//YYCCTestbench::StringTestbench();
|
//YYCCTestbench::StringTestbench();
|
||||||
//YYCCTestbench::ParserTestbench();
|
//YYCCTestbench::ParserTestbench();
|
||||||
//YYCCTestbench::DialogTestbench();
|
//YYCCTestbench::DialogTestbench();
|
||||||
|
YYCCTestbench::WinFctTestbench();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user