diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fca7dcb..964b4b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,13 +4,13 @@ add_library(YYCCommonplace STATIC "") target_sources(YYCCommonplace PRIVATE # Sources + COMHelper.cpp ConsoleHelper.cpp DialogHelper.cpp EncodingHelper.cpp ExceptionHelper.cpp FsPathPatch.cpp IOHelper.cpp - ParserHelper.cpp StringHelper.cpp WinFctHelper.cpp ) @@ -20,6 +20,7 @@ FILE_SET HEADERS FILES # Headers # Common headers + COMHelper.hpp ConsoleHelper.hpp DialogHelper.hpp EncodingHelper.hpp diff --git a/src/COMHelper.cpp b/src/COMHelper.cpp new file mode 100644 index 0000000..2453a7c --- /dev/null +++ b/src/COMHelper.cpp @@ -0,0 +1,40 @@ +#include "COMHelper.hpp" +#if YYCC_OS == YYCC_OS_WINDOWS + +namespace YYCC::COMHelper { + + /** + * @brief The guard for initialize COM environment. + * @details This class will try initializing COM environment by calling CoInitialize when constructing, + * and it also will try uninitializing COM environment when destructing. + * If initialization failed, uninitialization will not be executed. + */ + class ComGuard { + public: + ComGuard() : m_HasInit(false) { + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (SUCCEEDED(hr)) m_HasInit = true; + } + ~ComGuard() { + if (m_HasInit) { + CoUninitialize(); + } + } + + protected: + bool m_HasInit; + }; + + /** + * @brief The instance of COM environment guard. + * @details Dialog related function need COM environment, + * so we need initializing COM environment when loading this module, + * and uninitializing COM environment when we no longer use this module. + * So we use a static instance in here. + * And make it be const so no one can change it. + */ + static const ComGuard c_ComGuard; + +} + +#endif \ No newline at end of file diff --git a/src/COMHelper.hpp b/src/COMHelper.hpp new file mode 100644 index 0000000..c847829 --- /dev/null +++ b/src/COMHelper.hpp @@ -0,0 +1,69 @@ +#pragma once +#include "YYCCInternal.hpp" +#if YYCC_OS == YYCC_OS_WINDOWS + +#include + +#include "WinImportPrefix.hpp" +#include +#include +#include "WinImportSuffix.hpp" + +/** + * @brief COM fucntions related namespace. + * @details + * This namespace is Windows specific and it will disappear on other platforms. + * + * This namespace contain a COM Guard which make sure COM was initialized in current module when loading current module. + * It is essential because all calling to COM functions should be under the premise that COM has been initialized. + * This guard also will uninitialize COM when unloading this module. + * + * This namespace also provided various memory-safe types for interacting with COM functions. + * Although Microsoft also has similar smart pointer called CComPtr. + * But this library is eager to hide all Microsoft-related functions calling. + * Using CComPtr is not corresponding with the philosophy of this library. + * So these std-based smart pointer type were created. + * + * This namespace is used by internal functions as intended. + * They should not be used outside of this library. + * But if you compel to use them, it is also okey. +*/ +namespace YYCC::COMHelper { + + /** + * @brief C++ standard deleter for every COM interfaces inheriting IUnknown. + */ + class ComPtrDeleter { + public: + ComPtrDeleter() {} + void operator() (IUnknown* com_ptr) { + if (com_ptr != nullptr) { + com_ptr->Release(); + } + } + }; + + using SmartIFileDialog = std::unique_ptr; + using SmartIFileOpenDialog = std::unique_ptr; + using SmartIShellItem = std::unique_ptr; + using SmartIShellItemArray = std::unique_ptr; + using SmartIShellFolder = std::unique_ptr; + + /** + * @brief C++ standard deleter for almost raw pointer used in COM which need to be free by CoTaskMemFree() + */ + class CoTaskMemDeleter { + public: + CoTaskMemDeleter() {} + void operator() (void* com_ptr) { + if (com_ptr != nullptr) { + CoTaskMemFree(com_ptr); + } + } + }; + + using SmartLPWSTR = std::unique_ptr, CoTaskMemDeleter>; + +} + +#endif diff --git a/src/DialogHelper.cpp b/src/DialogHelper.cpp index 3d304cd..d9f41e2 100644 --- a/src/DialogHelper.cpp +++ b/src/DialogHelper.cpp @@ -6,43 +6,6 @@ namespace YYCC::DialogHelper { -#pragma region COM Guard - - /** - * @brief The guard for initialize COM environment. - * @details This class will try initializing COM environment by calling CoInitialize when constructing, - * and it also will try uninitializing COM environment when destructing. - * If initialization failed, uninitialization will not be executed. - */ - class ComGuard { - public: - ComGuard() : m_HasInit(false) { - HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - if (SUCCEEDED(hr)) m_HasInit = true; - } - ~ComGuard() { - if (m_HasInit) { - CoUninitialize(); - } - } - - protected: - bool m_HasInit; - }; - - /** - * @brief The instance of COM environment guard. - * @details Dialog related function need COM environment, - * so we need initializing COM environment when loading this module, - * and uninitializing COM environment when we no longer use this module. - * So we use a static instance in here. - * And make it be const so no one can change it. - */ - static const ComGuard c_ComGuard; - -#pragma endregion - - #pragma region FileFilters bool FileFilters::Add(const char* filter_name, std::initializer_list il) { @@ -185,7 +148,7 @@ namespace YYCC::DialogHelper { LPWSTR _name; HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name); if (FAILED(hr)) return false; - SmartLPWSTR display_name(_name); + COMHelper::SmartLPWSTR display_name(_name); // convert result if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret)) @@ -235,7 +198,7 @@ namespace YYCC::DialogHelper { ); if (FAILED(hr)) return false; // create memory-safe dialog pointer - SmartIFileDialog pfd(_pfd); + COMHelper::SmartIFileDialog pfd(_pfd); // set options for dialog // before setting, always get the options first in order. @@ -320,7 +283,7 @@ namespace YYCC::DialogHelper { IShellItem* _item; hr = pfd->GetResult(&_item); if (FAILED(hr)) return false; - SmartIShellItem result_item(_item); + COMHelper::SmartIShellItem result_item(_item); // extract display name std::string result_name; @@ -338,13 +301,13 @@ namespace YYCC::DialogHelper { IFileOpenDialog* _pfod = nullptr; hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod)); if (FAILED(hr)) return false; - SmartIFileOpenDialog pfod(_pfod); + COMHelper::SmartIFileOpenDialog pfod(_pfod); // obtain multiple file entires IShellItemArray* _items; hr = pfod->GetResults(&_items); if (FAILED(hr)) return false; - SmartIShellItemArray result_items(_items); + COMHelper::SmartIShellItemArray result_items(_items); // analyze file entries // get array count first @@ -357,7 +320,7 @@ namespace YYCC::DialogHelper { IShellItem* _item;; hr = result_items->GetItemAt(i, &_item); if (FAILED(hr)) return false; - SmartIShellItem result_item(_item); + COMHelper::SmartIShellItem result_item(_item); // extract display name std::string result_name; diff --git a/src/DialogHelper.hpp b/src/DialogHelper.hpp index 0f4cace..b5bc554 100644 --- a/src/DialogHelper.hpp +++ b/src/DialogHelper.hpp @@ -2,10 +2,10 @@ #include "YYCCInternal.hpp" #if YYCC_OS == YYCC_OS_WINDOWS +#include "COMHelper.hpp" #include #include #include -#include #include "WinImportPrefix.hpp" #include @@ -14,44 +14,6 @@ namespace YYCC::DialogHelper { -#pragma region COM Pointer Management - - /** - * @brief C++ standard deleter for every COM interfaces inheriting IUnknown. - */ - class ComPtrDeleter { - public: - ComPtrDeleter() {} - void operator() (IUnknown* com_ptr) { - if (com_ptr != nullptr) { - com_ptr->Release(); - } - } - }; - - using SmartIFileDialog = std::unique_ptr; - using SmartIFileOpenDialog = std::unique_ptr; - using SmartIShellItem = std::unique_ptr; - using SmartIShellItemArray = std::unique_ptr; - using SmartIShellFolder = std::unique_ptr; - - /** - * @brief C++ standard deleter for almost raw pointer used in COM which need to be free by CoTaskMemFree() - */ - class CoTaskMemDeleter { - public: - CoTaskMemDeleter() {} - void operator() (void* com_ptr) { - if (com_ptr != nullptr) { - CoTaskMemFree(com_ptr); - } - } - }; - - using SmartLPWSTR = std::unique_ptr, CoTaskMemDeleter>; - -#pragma endregion - /** * @brief The class represent the file types region in file dialog * @details THis class is specific for Windows use, not user oriented. @@ -169,7 +131,7 @@ namespace YYCC::DialogHelper { UINT m_WinDefaultFileTypeIndex; bool m_HasTitle, m_HasInitFileName; std::wstring m_WinTitle, m_WinInitFileName; - SmartIShellItem m_WinInitDirectory; + COMHelper::SmartIShellItem m_WinInitDirectory; void Clear() { m_WinOwner = nullptr; diff --git a/src/ParserHelper.cpp b/src/ParserHelper.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/WinFctHelper.cpp b/src/WinFctHelper.cpp index 834fa8d..eedf280 100644 --- a/src/WinFctHelper.cpp +++ b/src/WinFctHelper.cpp @@ -2,6 +2,7 @@ #if YYCC_OS == YYCC_OS_WINDOWS #include "EncodingHelper.hpp" +#include "COMHelper.hpp" namespace YYCC::WinFctHelper { @@ -70,6 +71,17 @@ namespace YYCC::WinFctHelper { return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str(), ret); } + bool GetLocalAppData(std::string& ret) { + // fetch path + LPWSTR _known_path; + HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &_known_path); + if (FAILED(hr)) return false; + COMHelper::SmartLPWSTR known_path(_known_path); + + // convert to utf8 + return YYCC::EncodingHelper::WcharToUTF8(known_path.get(), ret); + } + } #endif diff --git a/src/WinFctHelper.hpp b/src/WinFctHelper.hpp index ad6652f..21a6fe6 100644 --- a/src/WinFctHelper.hpp +++ b/src/WinFctHelper.hpp @@ -49,6 +49,16 @@ namespace YYCC::WinFctHelper { * @return True if success, otherwise false. */ bool GetModuleFileName(HINSTANCE hModule, std::string& ret); + + /** + * @brief Get the path to LOCALAPPDATA. + * @details LOCALAPPDATA usually was used as putting local app data files + * @param[out] ret + * The variable receiving UTF8 encoded path to LOCALAPPDATA. + * @return + */ + bool GetLocalAppData(std::string& ret); + } #endif diff --git a/testbench/main.cpp b/testbench/main.cpp index 4a4446d..44e7bd9 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -217,6 +217,16 @@ namespace YYCCTestbench { } } + static void ExceptionTestbench() { + YYCC::ExceptionHelper::Register(); + + // Perform a div zero exception. + int i = 1, j = 0; + int k = i / j; + + YYCC::ExceptionHelper::Unregister(); + } + static void WinFctTestbench() { Console::FormatLine("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, YYCC::WinFctHelper::GetCurrentModule()); @@ -256,6 +266,7 @@ int main(int argc, char** args) { //YYCCTestbench::StringTestbench(); //YYCCTestbench::ParserTestbench(); //YYCCTestbench::DialogTestbench(); + //YYCCTestbench::ExceptionTestbench(); //YYCCTestbench::WinFctTestbench(); - YYCCTestbench::FsPathPatch(); + //YYCCTestbench::FsPathPatch(); }