refactor: move COM type and guard into independent file

- move COM types and guard into independent file and namespace COMHelper because not only dialog, but also other parts also need to use COM related fucntion.
- remove ParserHelper.cpp because it is empty (ParserHelper is header only namespace).
- Add a function fetching LOCALAPPDATA in WinFctHelper.
This commit is contained in:
yyc12345 2024-06-17 12:46:32 +08:00
parent 8465d80a54
commit e20c03a5f1
9 changed files with 153 additions and 85 deletions

View File

@ -4,13 +4,13 @@ add_library(YYCCommonplace STATIC "")
target_sources(YYCCommonplace target_sources(YYCCommonplace
PRIVATE PRIVATE
# Sources # Sources
COMHelper.cpp
ConsoleHelper.cpp ConsoleHelper.cpp
DialogHelper.cpp DialogHelper.cpp
EncodingHelper.cpp EncodingHelper.cpp
ExceptionHelper.cpp ExceptionHelper.cpp
FsPathPatch.cpp FsPathPatch.cpp
IOHelper.cpp IOHelper.cpp
ParserHelper.cpp
StringHelper.cpp StringHelper.cpp
WinFctHelper.cpp WinFctHelper.cpp
) )
@ -20,6 +20,7 @@ FILE_SET HEADERS
FILES FILES
# Headers # Headers
# Common headers # Common headers
COMHelper.hpp
ConsoleHelper.hpp ConsoleHelper.hpp
DialogHelper.hpp DialogHelper.hpp
EncodingHelper.hpp EncodingHelper.hpp

40
src/COMHelper.cpp Normal file
View File

@ -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

69
src/COMHelper.hpp Normal file
View File

@ -0,0 +1,69 @@
#pragma once
#include "YYCCInternal.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS
#include <memory>
#include "WinImportPrefix.hpp"
#include <Windows.h>
#include <shlobj_core.h>
#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<IFileDialog, ComPtrDeleter>;
using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
using SmartIShellFolder = std::unique_ptr<IShellFolder, ComPtrDeleter>;
/**
* @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<std::remove_pointer_t<LPWSTR>, CoTaskMemDeleter>;
}
#endif

View File

@ -6,43 +6,6 @@
namespace YYCC::DialogHelper { 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 #pragma region FileFilters
bool FileFilters::Add(const char* filter_name, std::initializer_list<const char*> il) { bool FileFilters::Add(const char* filter_name, std::initializer_list<const char*> il) {
@ -185,7 +148,7 @@ namespace YYCC::DialogHelper {
LPWSTR _name; LPWSTR _name;
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name); HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
if (FAILED(hr)) return false; if (FAILED(hr)) return false;
SmartLPWSTR display_name(_name); COMHelper::SmartLPWSTR display_name(_name);
// convert result // convert result
if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret)) if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret))
@ -235,7 +198,7 @@ namespace YYCC::DialogHelper {
); );
if (FAILED(hr)) return false; if (FAILED(hr)) return false;
// create memory-safe dialog pointer // create memory-safe dialog pointer
SmartIFileDialog pfd(_pfd); COMHelper::SmartIFileDialog pfd(_pfd);
// set options for dialog // set options for dialog
// before setting, always get the options first in order. // before setting, always get the options first in order.
@ -320,7 +283,7 @@ namespace YYCC::DialogHelper {
IShellItem* _item; IShellItem* _item;
hr = pfd->GetResult(&_item); hr = pfd->GetResult(&_item);
if (FAILED(hr)) return false; if (FAILED(hr)) return false;
SmartIShellItem result_item(_item); COMHelper::SmartIShellItem result_item(_item);
// extract display name // extract display name
std::string result_name; std::string result_name;
@ -338,13 +301,13 @@ namespace YYCC::DialogHelper {
IFileOpenDialog* _pfod = nullptr; IFileOpenDialog* _pfod = nullptr;
hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod)); hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod));
if (FAILED(hr)) return false; if (FAILED(hr)) return false;
SmartIFileOpenDialog pfod(_pfod); COMHelper::SmartIFileOpenDialog pfod(_pfod);
// obtain multiple file entires // obtain multiple file entires
IShellItemArray* _items; IShellItemArray* _items;
hr = pfod->GetResults(&_items); hr = pfod->GetResults(&_items);
if (FAILED(hr)) return false; if (FAILED(hr)) return false;
SmartIShellItemArray result_items(_items); COMHelper::SmartIShellItemArray result_items(_items);
// analyze file entries // analyze file entries
// get array count first // get array count first
@ -357,7 +320,7 @@ namespace YYCC::DialogHelper {
IShellItem* _item;; IShellItem* _item;;
hr = result_items->GetItemAt(i, &_item); hr = result_items->GetItemAt(i, &_item);
if (FAILED(hr)) return false; if (FAILED(hr)) return false;
SmartIShellItem result_item(_item); COMHelper::SmartIShellItem result_item(_item);
// extract display name // extract display name
std::string result_name; std::string result_name;

View File

@ -2,10 +2,10 @@
#include "YYCCInternal.hpp" #include "YYCCInternal.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS #if YYCC_OS == YYCC_OS_WINDOWS
#include "COMHelper.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
#include <initializer_list> #include <initializer_list>
#include <memory>
#include "WinImportPrefix.hpp" #include "WinImportPrefix.hpp"
#include <Windows.h> #include <Windows.h>
@ -14,44 +14,6 @@
namespace YYCC::DialogHelper { 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<IFileDialog, ComPtrDeleter>;
using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
using SmartIShellFolder = std::unique_ptr<IShellFolder, ComPtrDeleter>;
/**
* @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<std::remove_pointer_t<LPWSTR>, CoTaskMemDeleter>;
#pragma endregion
/** /**
* @brief The class represent the file types region in file dialog * @brief The class represent the file types region in file dialog
* @details THis class is specific for Windows use, not user oriented. * @details THis class is specific for Windows use, not user oriented.
@ -169,7 +131,7 @@ namespace YYCC::DialogHelper {
UINT m_WinDefaultFileTypeIndex; UINT m_WinDefaultFileTypeIndex;
bool m_HasTitle, m_HasInitFileName; bool m_HasTitle, m_HasInitFileName;
std::wstring m_WinTitle, m_WinInitFileName; std::wstring m_WinTitle, m_WinInitFileName;
SmartIShellItem m_WinInitDirectory; COMHelper::SmartIShellItem m_WinInitDirectory;
void Clear() { void Clear() {
m_WinOwner = nullptr; m_WinOwner = nullptr;

View File

View File

@ -2,6 +2,7 @@
#if YYCC_OS == YYCC_OS_WINDOWS #if YYCC_OS == YYCC_OS_WINDOWS
#include "EncodingHelper.hpp" #include "EncodingHelper.hpp"
#include "COMHelper.hpp"
namespace YYCC::WinFctHelper { namespace YYCC::WinFctHelper {
@ -70,6 +71,17 @@ namespace YYCC::WinFctHelper {
return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str(), ret); 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 #endif

View File

@ -49,6 +49,16 @@ namespace YYCC::WinFctHelper {
* @return True if success, otherwise false. * @return True if success, otherwise false.
*/ */
bool GetModuleFileName(HINSTANCE hModule, std::string& ret); 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 #endif

View File

@ -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() { static void WinFctTestbench() {
Console::FormatLine("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, YYCC::WinFctHelper::GetCurrentModule()); 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::StringTestbench();
//YYCCTestbench::ParserTestbench(); //YYCCTestbench::ParserTestbench();
//YYCCTestbench::DialogTestbench(); //YYCCTestbench::DialogTestbench();
//YYCCTestbench::ExceptionTestbench();
//YYCCTestbench::WinFctTestbench(); //YYCCTestbench::WinFctTestbench();
YYCCTestbench::FsPathPatch(); //YYCCTestbench::FsPathPatch();
} }