From 84228b5f8c0f7e17e6c949c8b1f7abc079a2987a Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Thu, 23 May 2024 09:37:41 +0800 Subject: [PATCH] feat: add some work - add dialog parameter class for dialog helper. prepare to write real dialog popup function. - refactor file filters class in dialog helper to make it no const syntax conflict. - improve windows header import prefix and suffix header. remove the barrier limiting headers only can be imported once. fix define preprocessor usage bug. - move crt warning and errors macro from project settings to internal headers. - copy IronPad as ExceptionHandler but not adapted to this project. --- src/DialogHelper.cpp | 39 ++-- src/DialogHelper.hpp | 134 ++++++++---- src/ExceptionHelper.cpp | 325 +++++++++++++++++++++++++++++ src/ExceptionHelper.hpp | 20 ++ src/WinImportPrefix.hpp | 10 +- src/WinImportSuffix.hpp | 7 +- src/YYCCInternal.hpp | 14 ++ src/YYCCommonplace.vcxproj | 10 +- src/YYCCommonplace.vcxproj.filters | 6 + testbench/main.cpp | 5 +- 10 files changed, 500 insertions(+), 70 deletions(-) create mode 100644 src/ExceptionHelper.cpp create mode 100644 src/ExceptionHelper.hpp diff --git a/src/DialogHelper.cpp b/src/DialogHelper.cpp index 8bd66ae..eab41f7 100644 --- a/src/DialogHelper.cpp +++ b/src/DialogHelper.cpp @@ -27,53 +27,52 @@ namespace YYCC::DialogHelper { return true; } - void FileFilters::Clear() { - m_Filters.clear(); - } + bool FileFilters::Generate(WinFileFilters& win_result) const { + // clear Windows oriented data + win_result.m_WinDataStruct.reset(); + win_result.m_WinFilters.clear(); - bool FileFilters::Generate(UINT& filter_count, COMDLG_FILTERSPEC*& filter_specs) { - // init defualt value to prevent the scenario that caller do not check return value. - filter_count = 0u; - filter_specs = nullptr; - - // clear win string vector and build new one - m_WinFilters.clear(); + // build new Windows oriented string vector first for (const auto& it : m_Filters) { // convert name to wchar - WinFilterName name; + WinFileFilters::WinFilterName name; if (!YYCC::EncodingHelper::UTF8ToWchar(it.first.c_str(), name)) return false; // convert pattern and join them std::string joined_modes(YYCC::StringHelper::Join(it.second, u8";")); - WinFilterModes modes; + WinFileFilters::WinFilterModes modes; if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes.c_str(), modes)) return false; // append new pair - m_WinFilters.emplace_back(std::make_pair(name, modes)); + win_result.m_WinFilters.emplace_back(std::make_pair(name, modes)); } // check filter size // if it overflow the maximum value, return false - size_t count = m_WinFilters.size(); + size_t count = win_result.m_WinFilters.size(); if (count > std::numeric_limits::max()) return false; // create new win data struct // and assign string pointer from internal built win string vector. - m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]); + win_result.m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]); for (size_t i = 0u; i < count; ++i) { - m_WinDataStruct[i].pszName = m_WinFilters[i].first.c_str(); - m_WinDataStruct[i].pszSpec = m_WinFilters[i].second.c_str(); + win_result.m_WinDataStruct[i].pszName = win_result.m_WinFilters[i].first.c_str(); + win_result.m_WinDataStruct[i].pszSpec = win_result.m_WinFilters[i].second.c_str(); } - // set return value - filter_count = static_cast(count); - filter_specs = m_WinDataStruct.get(); + // everything is okey return true; } +#pragma endregion + +#pragma region FileDialog + + + #pragma endregion diff --git a/src/DialogHelper.hpp b/src/DialogHelper.hpp index 850ed24..8d3f93e 100644 --- a/src/DialogHelper.hpp +++ b/src/DialogHelper.hpp @@ -15,15 +15,39 @@ namespace YYCC::DialogHelper { + /** + * @brief The class represent the file types region in file dialog + * @details THis class is specific for Windows use, not user oriented. + */ + class WinFileFilters { + friend class FileFilters; + public: + WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {} + + UINT GetFilterCount() const { + return static_cast(m_WinFilters.size()); + } + const COMDLG_FILTERSPEC* GetFilterSpecs() const { + return m_WinDataStruct.get(); + } + + protected: + using WinFilterModes = std::wstring; + using WinFilterName = std::wstring; + using WinFilterPair = std::pair; + + std::vector m_WinFilters; + std::unique_ptr m_WinDataStruct; + }; + /** * @brief The class represent the file types region in file dialog. + * @details THis class is user oriented. User can use function manipulate file types + * and final fialog function will produce Windows-understood data struct from this. */ class FileFilters { public: - FileFilters() : - m_Filters(), - m_WinFilters(), m_WinDataStruct(nullptr) - {} + FileFilters() : m_Filters(){} /** * @brief Add a filter pair in file types list. @@ -40,16 +64,19 @@ namespace YYCC::DialogHelper { /** * @brief Clear filter pairs for following re-use. */ - void Clear(); + void Clear() { m_Filters.clear(); } + /** + * @brief Get the count of added filter pairs. + * @return The count of already added filter pairs. + */ + size_t Count() const { return m_Filters.size(); } /** * @brief Generate Windows dialog system used data struct. - * @param filter_count[out] The count of generated filter data struct. - * @param filter_specs[out] The pointer to generated filter data struct. - * @remarks User should not call this function. This function is used by internal functions. + * @param win_result[out] The class holding the generated filter data struct. * @return True if generation is success, otherwise false. */ - bool Generate(UINT& filter_count, COMDLG_FILTERSPEC*& filter_specs); + bool Generate(WinFileFilters& win_result) const; protected: using FilterModes = std::vector; @@ -57,45 +84,70 @@ namespace YYCC::DialogHelper { using FilterPair = std::pair; std::vector m_Filters; - - using WinFilterModes = std::wstring; - using WinFilterName = std::wstring; - using WinFilterPair = std::pair; - - std::vector m_WinFilters; - std::unique_ptr m_WinDataStruct; }; - //struct FileDialogParameter { - // FileDialogParameter() : - // m_Owner(nullptr), - // m_Filter(), m_SelectedFilter(0), - // m_Title(), - // m_DefaultExtension(), m_InitialDirectory(), m_InitialFileName() {} + class FileDialog { + public: + FileDialog() : + m_Owner(NULL), m_Title(), + m_FileTypes(), + m_DefaultFileTypeIndex(0u), + m_InitFileName(), m_InitDirectory() {} - // HWND m_Owner; - // std::vector> m_Filter; - // size_t m_SelectedFilter; - // std::string m_Title; - // std::string m_DefaultExtension; - // std::string m_InitialFileName; - // std::string m_InitialDirectory; - //}; + void SetOwner(HWND owner) { m_Owner = owner; } + HWND GetOwner() const { return m_Owner; } - //struct FolderDialogParameter { - // FolderDialogParameter() : - // m_Owner(nullptr), - // m_Title() {} + void SetTitle(const char* title) { + if (title == nullptr) m_Title.clear(); + else m_Title = title; + } + const char* GetTitle() const { + if (m_Title.empty()) return nullptr; + else return m_Title.c_str(); + } - // HWND m_Owner; - // std::string m_Title; - //}; + FileFilters& GetFileTypes() { + return m_FileTypes; + } + const FileFilters& GetFileTypes() const { + return m_FileTypes; + } - //bool OpenFileDialog(const FileDialogParameter& params, std::string& ret); - //bool OpenMultipleFileDialog(const FileDialogParameter& params, std::vector& ret); - //bool SaveFileDialog(const FileDialogParameter& params, std::string& ret); + void SetDefaultFileTypeIndex(UINT idx) { m_DefaultFileTypeIndex = idx; } + UINT GetDefaultFileTypeIndex() const { return m_DefaultFileTypeIndex; } + + void SetInitFileName(const char* init_filename) { + if (init_filename == nullptr) m_InitFileName.clear(); + else m_InitFileName = init_filename; + } + const char* GetInitFileName() const { + if (m_InitFileName.empty()) return nullptr; + else return m_InitFileName.c_str(); + } + + void SetInitDirectory(const char* init_dir) { + if (init_dir == nullptr) m_InitDirectory.clear(); + else m_InitDirectory = init_dir; + } + const char* GetInitDirectory() const { + if (m_InitDirectory.empty()) return nullptr; + else return m_InitDirectory.c_str(); + } - //bool OpenFolderDialog(const FolderDialogParameter& params, std::string& ret); + private: + HWND m_Owner; + std::string m_Title; + FileFilters m_FileTypes; + UINT m_DefaultFileTypeIndex; + std::string m_InitFileName; + std::string m_InitDirectory; + }; + + bool OpenFileDialog(const FileDialog& params, std::string& ret); + bool OpenMultipleFileDialog(const FileDialog& params, std::vector& ret); + bool SaveFileDialog(const FileDialog& params, std::string& ret); + + bool OpenFolderDialog(const FileDialog& params, std::string& ret); } diff --git a/src/ExceptionHelper.cpp b/src/ExceptionHelper.cpp new file mode 100644 index 0000000..b5f279f --- /dev/null +++ b/src/ExceptionHelper.cpp @@ -0,0 +1,325 @@ +#include "ExceptionHelper.hpp" +#if YYCC_OS == YYCC_OS_WINDOWS + +#include +#include +#include +#include + +#include "WinImportPrefix.hpp" +#include +#include +#include "WinImportSuffix.hpp" + +namespace YYCC::ExceptionHelper { + + /** + * @brief true if the exception handler already registered. + * This variable is served for singleton. + */ + static bool g_IsRegistered = false; + /** + * @brief true if a exception handler is running. + * This variable is served for blocking possible infinity recursive exception handling. + */ + static bool g_IsProcessing = false; + /** + * @brief The backup of original exception handler. + */ + LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup; + +#pragma region Exception Handler Detail + + 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) { + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: + return "access violation"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + return "array index out of bound"; + case EXCEPTION_BREAKPOINT: + return "breakpoint reached"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "misaligned data access"; + case EXCEPTION_FLT_DENORMAL_OPERAND: + return "operand had denormal value"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "floating-point division by zero"; + case EXCEPTION_FLT_INEXACT_RESULT: + return "no decimal fraction representation for value"; + case EXCEPTION_FLT_INVALID_OPERATION: + return "invalid floating-point operation"; + case EXCEPTION_FLT_OVERFLOW: + return "floating-point overflow"; + case EXCEPTION_FLT_STACK_CHECK: + return "floating-point stack corruption"; + case EXCEPTION_FLT_UNDERFLOW: + return "floating-point underflow"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "illegal instruction"; + case EXCEPTION_IN_PAGE_ERROR: + return "inaccessible page"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "integer division by zero"; + case EXCEPTION_INT_OVERFLOW: + return "integer overflow"; + case EXCEPTION_INVALID_DISPOSITION: + return "documentation says this should never happen"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + return "can't continue after a noncontinuable exception"; + case EXCEPTION_PRIV_INSTRUCTION: + return "attempted to execute a privileged instruction"; + case EXCEPTION_SINGLE_STEP: + return "one instruction has been executed"; + case EXCEPTION_STACK_OVERFLOW: + return "stack overflow"; + default: + return "unknown exception"; + } + } + + static void UExceptionFormat(std::FILE* fs, const char* fmt, ...) { + // write to file + va_list arg1; + va_start(arg1, fmt); + std::vfprintf(fs, fmt, arg1); + va_end(arg1); + // write to stdout + va_list arg2; + va_start(arg2, fmt); + std::vfprintf(stdout, fmt, arg2); + va_end(arg2); + } + + static void UExceptionPrint(std::FILE* fs, const char* strl) { + // write to file + std::fputs(strl, fs); + // write to stdout + std::fputs(strl, stdout); + } + + static void UExceptionBacktrace(FILE* fs, LPCONTEXT context, int maxdepth) { + // setup loading symbol options + SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); // lazy load symbol, and load line number. + + // setup handle + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + // init symbol + if (!SymInitialize(process, 0, TRUE)) { + // fail to load. return + UExceptionPrint(fs, "Lost symbol file!\n"); + return; + } + + // ========== CORE DUMP ========== + // prepare frame. setup correct fields + // references: + // https://github.com/rust-lang/backtrace-rs/blob/9ed25b581cfd2ee60e5a3b9054fd023bf6dced90/src/backtrace/dbghelp.rs + // https://sourceforge.net/p/predef/wiki/Architectures/ + DWORD machine_type = 0; + STACKFRAME64 frame; + memset(&frame, 0, sizeof(frame)); +#if defined(_M_IX86) || defined(__i386__) + // x86 + machine_type = IMAGE_FILE_MACHINE_I386; + frame.AddrPC.Offset = context->Eip; + frame.AddrStack.Offset = context->Esp; + frame.AddrFrame.Offset = context->Ebp; +#elif defined(_M_AMD64) || defined(__amd64__) + // amd64 + machine_type = IMAGE_FILE_MACHINE_AMD64; + frame.AddrPC.Offset = context->Rip; + frame.AddrStack.Offset = context->Rsp; + frame.AddrFrame.Offset = context->Rbp; +#elif defined(_M_ARM) || defined(__arm__) + // arm (32bit) + machine_type = IMAGE_FILE_MACHINE_ARMNT; + frame.AddrPC.Offset = context->Pc; + frame.AddrStack.Offset = context->Sp; + frame.AddrFrame.Offset = context->R11; +#elif defined(_M_ARM64) || defined(__aarch64__) + // arm64 + machine_type = IMAGE_FILE_MACHINE_ARM64; + frame.AddrPC.Offset = context->Pc; + frame.AddrStack.Offset = context->Sp; + frame.AddrFrame.Offset = context->DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp; +#else +#error "Unsupported platform" + //IA-64 anybody? + +#endif + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + + // other variables + char module_name_raw[MAX_PATH]; + + // stack walker + while (StackWalk64(machine_type, process, thread, &frame, context, + 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) { + + // depth breaker + --maxdepth; + if (maxdepth < 0) { + UExceptionPrint(fs, "...\n"); // indicate there are some frames not listed + break; + } + + // get module name + DWORD64 module_base = SymGetModuleBase64(process, frame.AddrPC.Offset); + const char* module_name = "[unknown module]"; + if (module_base && GetModuleFileNameA((HINSTANCE)module_base, module_name_raw, MAX_PATH)) { + module_name = module_name_raw; + } + + // get source file and line + const char* source_file = "[unknow_source_file]"; + DWORD64 source_file_line = 0; + DWORD dwDisplacement; + IMAGEHLP_LINE64 winline; + winline.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &dwDisplacement, &winline)) { + source_file = winline.FileName; + source_file_line = winline.LineNumber; + } + + // write to file + UExceptionFormat(fs, "0x%016llx(rel: 0x%016llx)[%s]\t%s#%llu\n", + frame.AddrPC.Offset, frame.AddrPC.Offset - module_base, module_name, + source_file, source_file_line + ); + + } + + // ========== END CORE DUMP ========== + + // free symbol + SymCleanup(process); + } + + static void UExceptionCoreDump(LPCWSTR filename, LPEXCEPTION_POINTERS info) { + // open file and write + HANDLE hFile = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION exception_info; + exception_info.ThreadId = GetCurrentThreadId(); + exception_info.ExceptionPointers = info; + exception_info.ClientPointers = TRUE; + MiniDumpWriteDump( + GetCurrentProcess(), GetCurrentProcessId(), hFile, + MiniDumpNormal, + &exception_info, + NULL, NULL + ); + CloseHandle(hFile); + } + } + + static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS info) { + // detect loop calling + if (g_IsProcessing) { + goto end_proc; + } + // start process + g_IsProcessing = true; + + { + // get main folder first + std::filesystem::path ironpad_path; + WCHAR module_path[MAX_PATH]; + std::memset(module_path, 0, sizeof(module_path)); + if (GetModuleFileNameW(GetCurrentModule(), module_path, MAX_PATH) == 0) { + goto failed; + } + ironpad_path = module_path; + ironpad_path = ironpad_path.parent_path(); + + // create 2 filename + auto logfilename = ironpad_path / "IronPad.log"; + auto dmpfilename = ironpad_path / "IronPad.dmp"; + std::fputc('\n', stdout); + std::fprintf(stdout, "Exception Log: %s\n", logfilename.string().c_str()); + std::fprintf(stdout, "Exception Coredump: %s\n", dmpfilename.string().c_str()); + + // output log file + { + std::FILE* fs = _wfopen(logfilename.wstring().c_str(), L"w"); + if (fs == nullptr) { + goto failed; + } + + // record exception type first + PEXCEPTION_RECORD rec = info->ExceptionRecord; + fprintf(fs, "Unhandled exception occured at 0x%08p: %s (%lu).\n", + rec->ExceptionAddress, + UExceptionGetCodeName(rec->ExceptionCode), + rec->ExceptionCode + ); + + // special proc for 2 exceptions + if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || rec->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) { + if (rec->NumberParameters >= 2) { + const char* op = + rec->ExceptionInformation[0] == 0 ? "read" : + rec->ExceptionInformation[0] == 1 ? "written" : "executed"; + fprintf(fs, "The data at memory address 0x%016" PRIxPTR " could not be %s.\n", + rec->ExceptionInformation[1], op); + } + } + + // output stacktrace + UExceptionBacktrace(fs, info->ContextRecord, 1024); + + std::fclose(fs); + } + + // output minidump + { + UExceptionCoreDump(dmpfilename.wstring().c_str(), info); + } + + } + + // end process + failed: + g_IsProcessing = false; + // if backup proc can be run, run it + // otherwise directly return. + end_proc: + if (g_ProcBackup != nullptr) { + return g_ProcBackup(info); + } else { + return EXCEPTION_CONTINUE_SEARCH; + } + } + +#pragma endregion + + void Register() { + if (g_IsRegistered) return; + g_ProcBackup = SetUnhandledExceptionFilter(UExceptionImpl); + g_IsRegistered = true; + } + + void Unregister() { + if (!g_IsRegistered) return; + SetUnhandledExceptionFilter(g_ProcBackup); + g_IsRegistered = false; + } + +} + +#endif diff --git a/src/ExceptionHelper.hpp b/src/ExceptionHelper.hpp new file mode 100644 index 0000000..25b731a --- /dev/null +++ b/src/ExceptionHelper.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "YYCCInternal.hpp" +#if YYCC_OS == YYCC_OS_WINDOWS + +namespace YYCC::ExceptionHelper { + + /** + * @brief Register unhandled exception handler + * @detail This function frequently called at the start of program. + */ + void Register(); + /** + * @brief Unregiister unhandled exception handler + * @detail This function frequently called at the end of program. + */ + void Unregister(); + +} + +#endif diff --git a/src/WinImportPrefix.hpp b/src/WinImportPrefix.hpp index 3dab6b0..8a19a87 100644 --- a/src/WinImportPrefix.hpp +++ b/src/WinImportPrefix.hpp @@ -1,11 +1,19 @@ -#pragma once +// It is by design that no pragma once or #if to prevent deplicated including. +// Because this header is the part of wrapper, not a real header. +// #pragma once + #include "YYCCInternal.hpp" #if YYCC_OS == YYCC_OS_WINDOWS // Define 2 macros to disallow Windows generate MIN and MAX macros // which cause std::min and std::max can not function as normal. +#if !defined(WIN32_LEAN_AND_MEAN) #define WIN32_LEAN_AND_MEAN +#endif + +#if !defined(NOMINMAX) #define NOMINMAX +#endif #endif \ No newline at end of file diff --git a/src/WinImportSuffix.hpp b/src/WinImportSuffix.hpp index 71d76f1..af2c6d2 100644 --- a/src/WinImportSuffix.hpp +++ b/src/WinImportSuffix.hpp @@ -1,4 +1,7 @@ -#pragma once +// It is by design that no pragma once or #if to prevent deplicated including. +// Because this header is the part of wrapper, not a real header. +// #pragma once + #include "YYCCInternal.hpp" #if YYCC_OS == YYCC_OS_WINDOWS @@ -6,6 +9,8 @@ // Windows also will generate following macros // which may cause the function sign is different in Windows and other platforms. // So we simply remove them. +// Because #undef will not throw error if there are no matched macro, +// so we simply #undef them directly. #undef GetObject #undef GetClassName #undef LoadImage diff --git a/src/YYCCInternal.hpp b/src/YYCCInternal.hpp index b307647..3cdc8b3 100644 --- a/src/YYCCInternal.hpp +++ b/src/YYCCInternal.hpp @@ -10,6 +10,20 @@ #define YYCC_OS YYCC_OS_LINUX #endif +// If we are in Windows, +// we need add 2 macros to disable Windows shitty warnings and errors of +// depracted functions and not secure functions. +#if YYCC_OS == YYCC_OS_WINDOWS + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif +#if !defined(_CRT_NONSTDC_NO_DEPRECATE) +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#endif + //// Decide the char type we used //#include //namespace YYCC { diff --git a/src/YYCCommonplace.vcxproj b/src/YYCCommonplace.vcxproj index 4c7be4c..294c40a 100644 --- a/src/YYCCommonplace.vcxproj +++ b/src/YYCCommonplace.vcxproj @@ -94,7 +94,7 @@ Level3 true - _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp17 /utf-8 %(AdditionalOptions) @@ -110,7 +110,7 @@ true true true - _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp17 /utf-8 %(AdditionalOptions) @@ -126,7 +126,7 @@ Level3 true - _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp17 /utf-8 %(AdditionalOptions) @@ -142,7 +142,7 @@ true true true - _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp17 /utf-8 %(AdditionalOptions) @@ -157,6 +157,7 @@ + @@ -169,6 +170,7 @@ + diff --git a/src/YYCCommonplace.vcxproj.filters b/src/YYCCommonplace.vcxproj.filters index 6046061..bce9462 100644 --- a/src/YYCCommonplace.vcxproj.filters +++ b/src/YYCCommonplace.vcxproj.filters @@ -45,6 +45,9 @@ Headers + + Headers + @@ -65,5 +68,8 @@ Sources + + Sources + \ No newline at end of file diff --git a/testbench/main.cpp b/testbench/main.cpp index 3574ce8..34489e3 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -60,9 +60,8 @@ namespace Testbench { test.Add("Text File (*.*)", {"*.txt"}); test.Add("All Files (*.*)", {"*.*"}); - UINT count; - COMDLG_FILTERSPEC* specs; - bool ret = test.Generate(count, specs); + YYCC::DialogHelper::WinFileFilters win_file_filters; + bool ret = test.Generate(win_file_filters); } }