├── HexTest ├── HexTest.h ├── res │ └── HexTest.ico ├── packages.config ├── stdafx.cpp ├── resource.h ├── AboutDlg.cpp ├── AboutDlg.h ├── HexTest.cpp ├── View.cpp ├── stdafx.h ├── View.h ├── MainFrm.h ├── MainFrm.cpp └── HexTest.vcxproj.filters ├── BasicDemo ├── BasicDemo.h ├── res │ └── BasicDemo.ico ├── packages.config ├── pch.cpp ├── AboutDlg.cpp ├── resource.h ├── AboutDlg.h ├── BasicDemo.cpp ├── pch.h ├── ProcessTreeListView.h ├── BasicDemo.vcxproj.filters ├── MainFrm.h ├── MainFrm.cpp └── ProcessTreeListView.cpp ├── WTLHelper ├── WTLHelper.rc ├── res │ ├── Collapsed.ico │ ├── Expanded.ico │ ├── Expanded2.ico │ └── Collapsed2.ico ├── WTLHelper.cpp ├── README.md ├── ClipboardHelper.h ├── DarkModeHelper.h ├── StringHelper.h ├── pch.cpp ├── ColorHelper.h ├── IconHelper.h ├── WTLHelperRes.h ├── ToolbarHelper.h ├── StringHelper.cpp ├── IBufferManager.h ├── pch.h ├── CustomEdit.h ├── VersionResourceHelper.h ├── ClipboardHelper.cpp ├── CustomListView.h ├── Theme.cpp ├── IconHelper.cpp ├── SortHelper.h ├── Selection.h ├── CustomTreeView.h ├── ThemeHelper.h ├── FrameView.h ├── ColorHelper.cpp ├── WTLx.h ├── CustomComboBox.h ├── ToolbarHelper.cpp ├── Theme.h ├── IniFile.h ├── SizeGrip.h ├── CustomRebar.h ├── VersionResourceHelper.cpp ├── Selection.cpp ├── CompoundFileReaderWriter.cpp ├── CustomTabView.h ├── CustomDialog.h ├── CustomToolBar.h ├── SortHelper.cpp ├── CustomButton.h ├── DialogHelper.h ├── IListView.cpp ├── CustomTabControl.h ├── CustomSplitterWindow.h ├── ListViewhelper.h ├── CompoundFile.h ├── IATHook.h ├── QuickFindEdit.h ├── CompoundFile.cpp ├── OwnerDrawnMenu.cpp ├── CompoundFileReaderWriter.h ├── IniFile.cpp ├── ColumnManager.h ├── CustomStatusBar.h ├── CustomTabView.cpp ├── ColumnManager.cpp ├── Settings.cpp ├── CustomHeader.h ├── TreeViewHelper.h ├── SortedFilteredVector.h ├── Settings.h ├── HexControl.h ├── WTLHelper.vcxproj.filters ├── ListViewhelper.cpp ├── OwnerDrawnMenu.h ├── ThemeHelper.cpp └── DarkModeHelper.cpp ├── packages.config ├── WTLHelper.sln ├── .gitattributes └── .gitignore /HexTest/HexTest.h: -------------------------------------------------------------------------------- 1 | // HexTest.h 2 | -------------------------------------------------------------------------------- /BasicDemo/BasicDemo.h: -------------------------------------------------------------------------------- 1 | // BasicDemo.h 2 | -------------------------------------------------------------------------------- /WTLHelper/WTLHelper.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/WTLHelper/WTLHelper.rc -------------------------------------------------------------------------------- /HexTest/res/HexTest.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/HexTest/res/HexTest.ico -------------------------------------------------------------------------------- /BasicDemo/res/BasicDemo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/BasicDemo/res/BasicDemo.ico -------------------------------------------------------------------------------- /WTLHelper/res/Collapsed.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/WTLHelper/res/Collapsed.ico -------------------------------------------------------------------------------- /WTLHelper/res/Expanded.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/WTLHelper/res/Expanded.ico -------------------------------------------------------------------------------- /WTLHelper/res/Expanded2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/WTLHelper/res/Expanded2.ico -------------------------------------------------------------------------------- /WTLHelper/res/Collapsed2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zodiacon/WTLHelper/HEAD/WTLHelper/res/Collapsed2.ico -------------------------------------------------------------------------------- /WTLHelper/WTLHelper.cpp: -------------------------------------------------------------------------------- 1 | // WTLHelper.cpp : Defines the functions for the static library. 2 | // 3 | 4 | #include "pch.h" 5 | -------------------------------------------------------------------------------- /WTLHelper/README.md: -------------------------------------------------------------------------------- 1 | # WTLHelper 2 | WTL helper library 3 | 4 | Something I use in my own tools (or working to port common parts to use WTLHelper). 5 | -------------------------------------------------------------------------------- /WTLHelper/ClipboardHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ClipboardHelper abstract final { 4 | static bool CopyText(HWND hWnd, PCWSTR text); 5 | }; 6 | 7 | -------------------------------------------------------------------------------- /BasicDemo/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /HexTest/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /WTLHelper/DarkModeHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct DarkModeHelper { 4 | static void Init(); 5 | static void AllowDarkModeForApp(bool allow); 6 | static void RefreshTitleBarThemeColor(HWND hWnd); 7 | }; 8 | 9 | -------------------------------------------------------------------------------- /WTLHelper/StringHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct StringHelper { 6 | static CString Format(PCWSTR format, ...); 7 | static CString& Format(CString& text, PCWSTR format, ...); 8 | }; 9 | -------------------------------------------------------------------------------- /WTLHelper/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /BasicDemo/pch.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // BasicDemo.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "pch.h" 6 | -------------------------------------------------------------------------------- /HexTest/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // HexTest.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | -------------------------------------------------------------------------------- /WTLHelper/ColorHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ColorHelper abstract final { 4 | static COLORREF Lighten(COLORREF color, int amount); 5 | static COLORREF Darken(COLORREF color, int amount); 6 | static bool IsSystemThemeDark(); 7 | }; 8 | -------------------------------------------------------------------------------- /WTLHelper/IconHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct IImageList2; 4 | 5 | struct IconHelper { 6 | static HICON GetStockIcon(SHSTOCKICONID id, bool big = false); 7 | static HICON GetShieldIcon(); 8 | static CComPtr CreateImageList(); 9 | }; 10 | -------------------------------------------------------------------------------- /WTLHelper/WTLHelperRes.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by WTLHelper.rc 4 | // 5 | #define IDI_COLLAPSED 10100 6 | #define IDI_COLLAPSED2 10101 7 | #define IDI_EXPANDED 10102 8 | #define IDI_EXPANDED2 10103 9 | 10 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /WTLHelper/ToolbarHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ToolBarButtonInfo { 4 | UINT id; 5 | int image; 6 | BYTE style = BTNS_BUTTON; 7 | PCWSTR text = nullptr; 8 | }; 9 | 10 | 11 | struct ToolbarHelper { 12 | static HWND CreateAndInitToolBar(HWND hWnd, const ToolBarButtonInfo* buttons, int count, int size = 24); 13 | static POINT GetDropdownMenuPoint(HWND hToolBar, UINT buttonId); 14 | }; 15 | -------------------------------------------------------------------------------- /WTLHelper/StringHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "StringHelper.h" 3 | 4 | CString StringHelper::Format(PCWSTR format, ...) { 5 | va_list argList; 6 | va_start(argList, format); 7 | CString text; 8 | text.Format(format, argList); 9 | va_end(argList); 10 | return text; 11 | } 12 | 13 | CString& StringHelper::Format(CString& text, PCWSTR format, ...) { 14 | va_list argList; 15 | va_start(argList, format); 16 | text.Format(format, argList); 17 | va_end(argList); 18 | return text; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /HexTest/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by HexTest.RC 4 | // 5 | 6 | 7 | #define IDD_ABOUTBOX 100 8 | #define IDR_MAINFRAME 128 9 | //#define IDR_HEXTESTTYPE 129 10 | 11 | // Next default values for new objects 12 | // 13 | #ifdef APSTUDIO_INVOKED 14 | #ifndef APSTUDIO_READONLY_SYMBOLS 15 | #define _APS_NEXT_RESOURCE_VALUE 201 16 | #define _APS_NEXT_CONTROL_VALUE 1000 17 | #define _APS_NEXT_SYMED_VALUE 101 18 | #define _APS_NEXT_COMMAND_VALUE 32775 19 | #endif 20 | #endif 21 | -------------------------------------------------------------------------------- /BasicDemo/AboutDlg.cpp: -------------------------------------------------------------------------------- 1 | // aboutdlg.cpp : implementation of the CAboutDlg class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "pch.h" 6 | #include "resource.h" 7 | #include "aboutdlg.h" 8 | 9 | LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { 10 | CenterWindow(GetParent()); 11 | return TRUE; 12 | } 13 | 14 | LRESULT CAboutDlg::OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 15 | EndDialog(wID); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /WTLHelper/IBufferManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct IBufferManager { 4 | virtual ~IBufferManager() = default; 5 | 6 | virtual uint32_t GetData(int64_t offset, uint8_t* buffer, uint32_t count) = 0; 7 | virtual bool Insert(int64_t offset, const uint8_t* data, uint32_t count) = 0; 8 | virtual bool Delete(int64_t offset, size_t count) = 0; 9 | virtual bool SetData(int64_t offset, const uint8_t* data, uint32_t count) = 0; 10 | virtual int64_t GetSize() const = 0; 11 | virtual uint8_t* GetRawData(int64_t offset) = 0; 12 | virtual bool IsReadOnly() const = 0; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /HexTest/AboutDlg.cpp: -------------------------------------------------------------------------------- 1 | // aboutdlg.cpp : implementation of the CAboutDlg class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "stdafx.h" 6 | #include "resource.h" 7 | 8 | #include "aboutdlg.h" 9 | 10 | LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 11 | { 12 | CenterWindow(GetParent()); 13 | return TRUE; 14 | } 15 | 16 | LRESULT CAboutDlg::OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 17 | { 18 | EndDialog(wID); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /WTLHelper/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | -------------------------------------------------------------------------------- /WTLHelper/CustomEdit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | 5 | class CCustomEdit : public CWindowImpl { 6 | public: 7 | void OnFinalMessage(HWND) override { 8 | delete this; 9 | } 10 | 11 | BEGIN_MSG_MAP(CCustomEdit) 12 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 13 | END_MSG_MAP() 14 | 15 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { 16 | CDCHandle dc((HDC)wParam); 17 | CRect rc; 18 | GetClientRect(&rc); 19 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 20 | return 1; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /WTLHelper/VersionResourceHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class VersionResourceHelper { 7 | public: 8 | explicit VersionResourceHelper(PCWSTR path = nullptr); 9 | explicit VersionResourceHelper(PVOID data); 10 | 11 | bool IsValid() const; 12 | operator bool() const { 13 | return IsValid(); 14 | } 15 | CString GetValue(const std::wstring& name) const; 16 | const CString& GetPath() const { 17 | return m_path; 18 | } 19 | 20 | private: 21 | std::unique_ptr m_buffer; 22 | BYTE const* m_Data{ nullptr }; 23 | CString m_path; 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /WTLHelper/ClipboardHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ClipboardHelper.h" 3 | 4 | bool ClipboardHelper::CopyText(HWND hWnd, PCWSTR text) { 5 | if (::OpenClipboard(hWnd)) { 6 | ::EmptyClipboard(); 7 | auto size = (::wcslen(text) + 1) * sizeof(WCHAR); 8 | auto hData = ::GlobalAlloc(GMEM_MOVEABLE, size); 9 | if (hData) { 10 | auto p = ::GlobalLock(hData); 11 | if (p) { 12 | ::memcpy(p, text, size); 13 | ::GlobalUnlock(p); 14 | ::SetClipboardData(CF_UNICODETEXT, hData); 15 | } 16 | } 17 | ::CloseClipboard(); 18 | if (hData) 19 | return true; 20 | } 21 | return false; 22 | } 23 | -------------------------------------------------------------------------------- /WTLHelper/CustomListView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Theme.h" 4 | 5 | class CCustomListView : public CWindowImpl { 6 | public: 7 | BEGIN_MSG_MAP(CCustomListView) 8 | MESSAGE_HANDLER(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), OnUpdateTheme) 9 | END_MSG_MAP() 10 | 11 | void OnFinalMessage(HWND) override { 12 | delete this; 13 | } 14 | 15 | LRESULT OnUpdateTheme(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/) { 16 | auto theme = reinterpret_cast(lParam); 17 | SetBkColor(theme->BackColor); 18 | SetTextColor(theme->TextColor); 19 | 20 | return 0; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /WTLHelper/Theme.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Theme.h" 3 | 4 | Theme::Theme(bool def) : _default(def) { 5 | for (auto& c : SysColors) 6 | c = CLR_INVALID; 7 | } 8 | 9 | bool Theme::IsDefault() const { 10 | return _default; 11 | } 12 | 13 | HBRUSH Theme::GetSysBrush(int index) const { 14 | if (_SysBrush[index] == nullptr || ::GetObjectType(_SysBrush[index]) != OBJ_BRUSH) { 15 | auto color = GetSysColor(index); 16 | if (color != CLR_INVALID) { 17 | _SysBrush[index].Detach(); 18 | _SysBrush[index].CreateSolidBrush(color); 19 | } 20 | } 21 | return _SysBrush[index].m_hBrush; 22 | } 23 | 24 | COLORREF Theme::GetSysColor(int index) const { 25 | return SysColors[index]; 26 | } 27 | -------------------------------------------------------------------------------- /WTLHelper/IconHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "IconHelper.h" 3 | 4 | HICON IconHelper::GetShieldIcon() { 5 | return GetStockIcon(SIID_SHIELD); 6 | } 7 | 8 | CComPtr IconHelper::CreateImageList() { 9 | CComPtr spImages; 10 | ImageList_CoCreateInstance(CLSID_ImageList, nullptr, __uuidof(IImageList2), reinterpret_cast(&spImages)); 11 | ATLASSERT(spImages); 12 | return spImages; 13 | } 14 | 15 | HICON IconHelper::GetStockIcon(SHSTOCKICONID id, bool big) { 16 | SHSTOCKICONINFO ssii = { sizeof(ssii) }; 17 | if (FAILED(::SHGetStockIconInfo(id, (big ? SHGSI_LARGEICON : SHGSI_SMALLICON) | SHGSI_ICON, &ssii))) 18 | return nullptr; 19 | 20 | return ssii.hIcon; 21 | } 22 | -------------------------------------------------------------------------------- /WTLHelper/SortHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct SortHelper final abstract { 7 | static bool Sort(const ATL::CString& s1, const ATL::CString& s2, bool ascending); 8 | static bool Sort(const std::string& s1, const std::string& s2, bool ascending); 9 | static bool Sort(const std::wstring& s1, const std::wstring& s2, bool ascending); 10 | static bool Sort(PCWSTR s1, PCWSTR s2, bool ascending); 11 | static bool Sort(PWSTR s1, PWSTR s2, bool ascending); 12 | static bool Sort(bool a, bool b, bool asc); 13 | 14 | template 15 | static bool Sort(const Number& n1, const Number& n2, bool ascending) { 16 | return ascending ? n2 > n1 : n2 < n1; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /WTLHelper/Selection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class SelectionType { 4 | Simple, 5 | Box 6 | }; 7 | 8 | class Selection { 9 | public: 10 | void SetSimple(int64_t offset, int64_t len); 11 | void SetBox(int64_t offset, int bytesPerLine, int width, int height); 12 | void SetAnchor(int64_t offset); 13 | 14 | int64_t GetOffset() const; 15 | int64_t GetAnchor() const; 16 | bool IsSelected(int64_t offset) const; 17 | bool IsEmpty() const; 18 | SelectionType GetSelectionType() const; 19 | int64_t GetLength() const; 20 | 21 | void Clear(); 22 | 23 | private: 24 | SelectionType m_Type{ SelectionType::Simple }; 25 | int64_t m_Offset{ -1 }, m_Anchor{ -1 }; 26 | int64_t m_Length{ 0 }; 27 | int m_Width, m_Height, m_BytesPerLine; 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /WTLHelper/CustomTreeView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Theme.h" 4 | 5 | struct CCustomTreeView : CWindowImpl { 6 | BEGIN_MSG_MAP(CCustomTreeView) 7 | MESSAGE_HANDLER(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), OnUpdateTheme) 8 | END_MSG_MAP() 9 | 10 | void OnFinalMessage(HWND) override { 11 | delete this; 12 | } 13 | 14 | void Init() { 15 | auto theme = ThemeHelper::GetCurrentTheme(); 16 | SetBkColor(theme->BackColor); 17 | SetTextColor(theme->TextColor); 18 | } 19 | 20 | LRESULT OnUpdateTheme(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/) { 21 | auto theme = reinterpret_cast(lParam); 22 | SetBkColor(theme->BackColor); 23 | SetTextColor(theme->TextColor); 24 | 25 | return 0; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /BasicDemo/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by BasicDemo.rc 4 | // 5 | #define IDD_ABOUTBOX 100 6 | #define IDR_MAINFRAME 128 7 | #define ID_WINDOW_CLOSE 32772 8 | #define ID_WINDOW_CLOSE_ALL 32773 9 | #define ID_OPTIONS_ALWAYSONTOP 32775 10 | #define ID_OPTIONS_DARKMODE 32776 11 | #define ID_DEMO_TREEVIEWANDLISTVIEW 32777 12 | #define ID_DEMO_TREELISTVIEW 32778 13 | 14 | // Next default values for new objects 15 | // 16 | #ifdef APSTUDIO_INVOKED 17 | #ifndef APSTUDIO_READONLY_SYMBOLS 18 | #define _APS_NEXT_RESOURCE_VALUE 201 19 | #define _APS_NEXT_COMMAND_VALUE 32779 20 | #define _APS_NEXT_CONTROL_VALUE 1000 21 | #define _APS_NEXT_SYMED_VALUE 101 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /WTLHelper/ThemeHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Theme; 4 | 5 | enum class DCOperation { 6 | None, 7 | SetTextColor = 1, 8 | }; 9 | DEFINE_ENUM_FLAG_OPERATORS(DCOperation); 10 | 11 | struct COwnerDrawnMenuBase; 12 | 13 | struct ThemeHelper abstract final { 14 | static bool SetNativeDarkMode(bool dark); 15 | static bool LoadFromFile(PCWSTR path, Theme& theme); 16 | static bool SaveToFile(Theme const& theme, PCWSTR path); 17 | static bool Init(HANDLE hThread = ::GetCurrentThread()); 18 | static int Suspend(); 19 | static bool IsSuspended(); 20 | static int Resume(); 21 | 22 | static const Theme* GetCurrentTheme(); 23 | static bool IsDefault(); 24 | static void SetCurrentTheme(const Theme& theme, HWND hWnd = nullptr); 25 | static void SetDefaultTheme(HWND hWnd); 26 | static void UpdateMenuColors(COwnerDrawnMenuBase& menu, bool dark); 27 | //static void SendMessageToDescendants(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /WTLHelper/FrameView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class CFrameView abstract : 5 | public CFrameWindowImpl { 6 | public: 7 | using BaseFrame = CFrameWindowImpl; 8 | explicit CFrameView(TFrame* frame) : m_pFrame(frame) {} 9 | 10 | void SetStatic(bool s = true) { 11 | m_Static = s; 12 | } 13 | 14 | TFrame* Frame() const { 15 | return m_pFrame; 16 | } 17 | 18 | void OnFinalMessage(HWND) override { 19 | if(!m_Static) 20 | delete this; 21 | } 22 | 23 | private: 24 | // Handler prototypes (uncomment arguments if needed): 25 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 26 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 27 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 28 | 29 | TFrame* m_pFrame; 30 | bool m_Static{ false }; 31 | }; 32 | -------------------------------------------------------------------------------- /WTLHelper/ColorHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ColorHelper.h" 3 | 4 | COLORREF ColorHelper::Lighten(COLORREF color, int amount) { 5 | return RGB( 6 | (BYTE)min(255, GetRValue(color) + 255 * amount / 100), 7 | (BYTE)min(255, GetGValue(color) + 255 * amount / 100), 8 | (BYTE)min(255, GetBValue(color) + 255 * amount / 100)); 9 | } 10 | 11 | COLORREF ColorHelper::Darken(COLORREF color, int amount) { 12 | return RGB( 13 | (BYTE)max(0, GetRValue(color) - 255 * amount / 100), 14 | (BYTE)max(0, GetGValue(color) - 255 * amount / 100), 15 | (BYTE)max(0, GetBValue(color) - 255 * amount / 100)); 16 | } 17 | 18 | bool ColorHelper::IsSystemThemeDark() { 19 | CRegKey key; 20 | key.Open(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", KEY_QUERY_VALUE); 21 | if (!key) 22 | return false; 23 | 24 | DWORD dark; 25 | return key.QueryDWORDValue(L"AppsUseLightTheme", dark) == ERROR_SUCCESS ? !dark : false; 26 | } 27 | -------------------------------------------------------------------------------- /HexTest/AboutDlg.h: -------------------------------------------------------------------------------- 1 | // aboutdlg.h : interface of the CAboutDlg class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | class CAboutDlg : public CDialogImpl 8 | { 9 | public: 10 | enum { IDD = IDD_ABOUTBOX }; 11 | 12 | BEGIN_MSG_MAP(CAboutDlg) 13 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 14 | COMMAND_ID_HANDLER(IDOK, OnCloseCmd) 15 | COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) 16 | END_MSG_MAP() 17 | 18 | // Handler prototypes (uncomment arguments if needed): 19 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 20 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 21 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 22 | 23 | LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 24 | LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 25 | }; 26 | -------------------------------------------------------------------------------- /BasicDemo/AboutDlg.h: -------------------------------------------------------------------------------- 1 | // aboutdlg.h : interface of the CAboutDlg class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | class CAboutDlg : public CDialogImpl 8 | { 9 | public: 10 | enum { IDD = IDD_ABOUTBOX }; 11 | 12 | BEGIN_MSG_MAP(CAboutDlg) 13 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 14 | COMMAND_ID_HANDLER(IDOK, OnCloseCmd) 15 | COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) 16 | END_MSG_MAP() 17 | 18 | // Handler prototypes (uncomment arguments if needed): 19 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 20 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 21 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 22 | 23 | LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 24 | LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 25 | }; 26 | -------------------------------------------------------------------------------- /WTLHelper/WTLx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class WindowStyles : uint32_t { 4 | None = 0, 5 | Child = WS_CHILD, 6 | Visible = WS_VISIBLE, 7 | Overlapped = WS_OVERLAPPED, 8 | MinimizeBox = WS_MINIMIZEBOX, 9 | Border = WS_BORDER, 10 | Caption = WS_CAPTION, 11 | SystemMenu = WS_SYSMENU, 12 | }; 13 | 14 | 15 | enum class WindowStylesEx : uint32_t { 16 | None = 0, 17 | }; 18 | 19 | 20 | class CWindowX : public CWindow { 21 | public: 22 | using CWindow::CWindow; 23 | 24 | HWND Create( 25 | _In_opt_z_ LPCTSTR lpstrWndClass, 26 | _In_opt_ HWND hWndParent, 27 | _In_ _U_RECT rect = nullptr, 28 | _In_opt_z_ LPCTSTR szWindowName = nullptr, 29 | _In_ WindowStyles dwStyle = WindowStyles::None, 30 | _In_ WindowStylesEx dwExStyle = WindowStylesEx::None, 31 | _In_ _U_MENUorID MenuOrID = 0U, 32 | _In_opt_ LPVOID lpCreateParam = nullptr) noexcept { 33 | return CWindow::Create(lpstrWndClass, hWndParent, rect, szWindowName, static_cast(dwStyle), 34 | static_cast(dwExStyle), MenuOrID, lpCreateParam); 35 | } 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /BasicDemo/BasicDemo.cpp: -------------------------------------------------------------------------------- 1 | // BasicDemo.cpp : main source file for BasicDemo.exe 2 | // 3 | 4 | #include "pch.h" 5 | #include "resource.h" 6 | #include "MainFrm.h" 7 | 8 | CAppModule _Module; 9 | 10 | int Run(LPTSTR /*lpstrCmdLine*/ = nullptr, int nCmdShow = SW_SHOWDEFAULT) { 11 | CMessageLoop theLoop; 12 | _Module.AddMessageLoop(&theLoop); 13 | 14 | CMainFrame wndMain; 15 | 16 | if (wndMain.CreateEx() == nullptr) { 17 | ATLTRACE(_T("Main window creation failed!\n")); 18 | return 0; 19 | } 20 | 21 | wndMain.ShowWindow(nCmdShow); 22 | 23 | int nRet = theLoop.Run(); 24 | 25 | _Module.RemoveMessageLoop(); 26 | return nRet; 27 | } 28 | 29 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { 30 | HRESULT hRes = ::CoInitialize(nullptr); 31 | ATLASSERT(SUCCEEDED(hRes)); 32 | 33 | AtlInitCommonControls(ICC_BAR_CLASSES | ICC_TREEVIEW_CLASSES | ICC_LISTVIEW_CLASSES); 34 | 35 | hRes = _Module.Init(nullptr, hInstance); 36 | ATLASSERT(SUCCEEDED(hRes)); 37 | 38 | int nRet = Run(lpstrCmdLine, nCmdShow); 39 | 40 | _Module.Term(); 41 | ::CoUninitialize(); 42 | 43 | return nRet; 44 | } 45 | -------------------------------------------------------------------------------- /WTLHelper/CustomComboBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | 5 | class CCustomComboBox : public CWindowImpl { 6 | public: 7 | void OnFinalMessage(HWND) override { 8 | delete this; 9 | } 10 | 11 | BEGIN_MSG_MAP(CCustomComboBox) 12 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 13 | END_MSG_MAP() 14 | 15 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { 16 | CDCHandle dc((HDC)wParam); 17 | CRect rc; 18 | GetClientRect(&rc); 19 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 20 | return 1; 21 | } 22 | }; 23 | 24 | class CCustomComboLBox : public CWindowImpl { 25 | public: 26 | void OnFinalMessage(HWND) override { 27 | delete this; 28 | } 29 | 30 | BEGIN_MSG_MAP(CCustomComboLBox) 31 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 32 | END_MSG_MAP() 33 | 34 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { 35 | CDCHandle dc((HDC)wParam); 36 | CRect rc; 37 | GetClientRect(&rc); 38 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 39 | return 1; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /HexTest/HexTest.cpp: -------------------------------------------------------------------------------- 1 | // HexTest.cpp : main source file for HexTest.exe 2 | // 3 | 4 | #include "stdafx.h" 5 | 6 | #include "resource.h" 7 | 8 | #include "View.h" 9 | #include "aboutdlg.h" 10 | #include "MainFrm.h" 11 | 12 | CAppModule _Module; 13 | 14 | int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT) { 15 | CMessageLoop theLoop; 16 | _Module.AddMessageLoop(&theLoop); 17 | 18 | CMainFrame wndMain; 19 | 20 | if (wndMain.CreateEx() == NULL) { 21 | ATLTRACE(_T("Main window creation failed!\n")); 22 | return 0; 23 | } 24 | 25 | wndMain.ShowWindow(nCmdShow); 26 | 27 | int nRet = theLoop.Run(); 28 | 29 | _Module.RemoveMessageLoop(); 30 | return nRet; 31 | } 32 | 33 | int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { 34 | HRESULT hRes = ::CoInitialize(NULL); 35 | ATLASSERT(SUCCEEDED(hRes)); 36 | 37 | AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls 38 | 39 | hRes = _Module.Init(NULL, hInstance); 40 | ATLASSERT(SUCCEEDED(hRes)); 41 | 42 | int nRet = Run(lpstrCmdLine, nCmdShow); 43 | 44 | _Module.Term(); 45 | ::CoUninitialize(); 46 | 47 | return nRet; 48 | } 49 | -------------------------------------------------------------------------------- /WTLHelper/ToolbarHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ToolbarHelper.h" 3 | 4 | HWND ToolbarHelper::CreateAndInitToolBar(HWND hWnd, const ToolBarButtonInfo* buttons, int count, int size) { 5 | CToolBarCtrl tb; 6 | auto hWndToolBar = tb.Create(hWnd, CWindow::rcDefault, nullptr, ATL_SIMPLE_TOOLBAR_PANE_STYLE | TBSTYLE_LIST, 0, ATL_IDW_TOOLBAR); 7 | tb.SetExtendedStyle(TBSTYLE_EX_MIXEDBUTTONS); 8 | 9 | CImageList tbImages; 10 | tbImages.Create(size, size, ILC_COLOR32, 4, 4); 11 | tb.SetImageList(tbImages); 12 | 13 | for (int i = 0; i < count; i++) { 14 | auto& b = buttons[i]; 15 | if (b.id == 0) 16 | tb.AddSeparator(0); 17 | else { 18 | int image = b.image == 0 ? I_IMAGENONE : tbImages.AddIcon(AtlLoadIconImage(b.image, 0, size, size)); 19 | tb.AddButton(b.id, b.style | (b.text ? BTNS_SHOWTEXT : 0), TBSTATE_ENABLED, image, b.text, 0); 20 | } 21 | } 22 | return hWndToolBar; 23 | } 24 | 25 | POINT ToolbarHelper::GetDropdownMenuPoint(HWND hToolBar, UINT buttonId) { 26 | CToolBarCtrl tb(hToolBar); 27 | CRect rect; 28 | tb.GetItemRect(tb.CommandToIndex(buttonId), &rect); 29 | CPoint pt(rect.left, rect.bottom); 30 | tb.MapWindowPoints(HWND_DESKTOP, &pt, 1); 31 | return pt; 32 | } 33 | -------------------------------------------------------------------------------- /WTLHelper/Theme.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Theme { 4 | explicit Theme(bool def = false); 5 | 6 | bool IsDefault() const; 7 | 8 | CString Name{ L"Default" }; 9 | COLORREF BackColor{ ::GetSysColor(COLOR_WINDOW) }; 10 | COLORREF TextColor{ ::GetSysColor(COLOR_WINDOWTEXT) }; 11 | 12 | struct { 13 | COLORREF TextColor{ ::GetSysColor(COLOR_WINDOWTEXT) }; 14 | COLORREF BackColor{ ::GetSysColor(COLOR_MENU) }; 15 | COLORREF SelectionTextColor{ ::GetSysColor(COLOR_HIGHLIGHTTEXT) }; 16 | COLORREF SelectionBackColor{ ::GetSysColor(COLOR_HIGHLIGHT) }; 17 | COLORREF SeparatorColor{ ::GetSysColor(COLOR_GRAYTEXT) }; 18 | } Menu; 19 | 20 | struct { 21 | COLORREF BackColor{ CLR_INVALID }; 22 | COLORREF TextColor{ CLR_INVALID }; 23 | COLORREF SelectedTextColor{ CLR_INVALID }; 24 | COLORREF SelectedBackColor{ CLR_INVALID }; 25 | } ListView; 26 | 27 | struct { 28 | COLORREF TextColor{ ::GetSysColor(COLOR_WINDOWTEXT) }; 29 | COLORREF BackColor{ ::GetSysColor(COLOR_WINDOW) }; 30 | } StatusBar; 31 | 32 | mutable COLORREF SysColors[32]; 33 | 34 | HBRUSH GetSysBrush(int index) const; 35 | COLORREF GetSysColor(int index) const; 36 | 37 | private: 38 | mutable CBrush _SysBrush[32]; 39 | bool _default; 40 | }; 41 | -------------------------------------------------------------------------------- /WTLHelper/IniFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class IniFile { 7 | public: 8 | IniFile(PCWSTR path); 9 | 10 | bool IsValid() const; 11 | 12 | CString ReadString(PCWSTR section, PCWSTR name, PCWSTR defaultValue = nullptr); 13 | int ReadInt(PCWSTR section, PCWSTR name, int defaultValue = 0); 14 | COLORREF ReadColor(PCWSTR section, PCWSTR name, COLORREF defaultValue = CLR_INVALID); 15 | std::vector ReadSection(PCWSTR section); 16 | bool ReadBool(PCWSTR section, PCWSTR name, bool defaultValue = false); 17 | bool ReadFont(PCWSTR section, PCWSTR name, LOGFONT& font); 18 | std::unique_ptr ReadBinary(PCWSTR section, PCWSTR name, unsigned& size); 19 | 20 | bool WriteString(PCWSTR section, PCWSTR name, PCWSTR value); 21 | bool WriteInt(PCWSTR section, PCWSTR name, int value, bool hex = false); 22 | bool WriteBool(PCWSTR section, PCWSTR name, bool value); 23 | bool WriteColor(PCWSTR section, PCWSTR name, COLORREF color); 24 | bool WriteFont(PCWSTR section, PCWSTR name, const LOGFONT& font); 25 | bool WriteBinary(PCWSTR section, PCWSTR name, void* data, unsigned size); 26 | 27 | protected: 28 | COLORREF ParseHexColor(const CString& hex); 29 | COLORREF ParseDecColor(const CString& text); 30 | 31 | private: 32 | CString _path; 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /WTLHelper/SizeGrip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | 5 | class CSizeGrip : public CWindowImpl { 6 | public: 7 | BEGIN_MSG_MAP(CSizeGrip) 8 | MESSAGE_HANDLER(WM_PAINT, OnPaint) 9 | END_MSG_MAP() 10 | 11 | void OnFinalMessage(HWND) override { 12 | delete this; 13 | } 14 | 15 | static void DrawSizeGrip(CWindow win, CRect& rc) { 16 | CClientDC dc(win); 17 | DrawSizeGrip(dc.m_hDC, rc); 18 | win.ValidateRect(nullptr); 19 | } 20 | 21 | static void DrawSizeGrip(CDCHandle dc, CRect& rc) { 22 | auto color = ThemeHelper::GetCurrentTheme()->StatusBar.TextColor; 23 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->StatusBar.BackColor); 24 | CPoint start(rc.left + 5, rc.top + 5); 25 | 26 | for (int y = 0; y < 3; y++) { 27 | for (int x = 0; x < 3; x++) { 28 | if (x + y < 2) 29 | continue; 30 | CRect r(CPoint(start.x + 4 * x, start.y + 4 * y), CSize(2, 2)); 31 | dc.FillSolidRect(&r, color); 32 | } 33 | } 34 | } 35 | 36 | LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 37 | auto theme = ThemeHelper::GetCurrentTheme(); 38 | ATLASSERT(theme); 39 | 40 | if (GetStyle() & (SBS_SIZEBOX | SBS_SIZEGRIP)) { 41 | CRect rc; 42 | GetClientRect(&rc); 43 | CPaintDC dc(m_hWnd); 44 | DrawSizeGrip(dc.m_hDC, rc); 45 | } 46 | else { 47 | bHandled = FALSE; 48 | } 49 | 50 | return 0; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /HexTest/View.cpp: -------------------------------------------------------------------------------- 1 | // View.cpp : implementation of the CView class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "stdafx.h" 6 | #include "resource.h" 7 | 8 | #include "View.h" 9 | 10 | BOOL CView::PreTranslateMessage(MSG* pMsg) { 11 | pMsg; 12 | return FALSE; 13 | } 14 | 15 | LRESULT CView::OnCreate(UINT, WPARAM, LPARAM, BOOL&) { 16 | m_Hex.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); 17 | 18 | return 0; 19 | } 20 | 21 | LRESULT CView::OnSize(UINT, WPARAM, LPARAM lp, BOOL&) { 22 | if (m_Hex) 23 | m_Hex.MoveWindow(0, 0, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); 24 | return LRESULT(); 25 | } 26 | 27 | LRESULT CView::OnEraseBkgnd(UINT, WPARAM, LPARAM, BOOL&) { 28 | return 1; 29 | } 30 | 31 | uint32_t SimpleBuffer::GetData(int64_t offset, uint8_t* buffer, uint32_t count) { 32 | return uint32_t(); 33 | } 34 | 35 | bool SimpleBuffer::Insert(int64_t offset, const uint8_t* data, uint32_t count) { 36 | return false; 37 | } 38 | 39 | bool SimpleBuffer::Delete(int64_t offset, size_t count) { 40 | return false; 41 | } 42 | 43 | bool SimpleBuffer::SetData(int64_t offset, const uint8_t* data, uint32_t count) { 44 | return false; 45 | } 46 | 47 | int64_t SimpleBuffer::GetSize() const { 48 | return 1 << 20; 49 | } 50 | 51 | uint8_t* SimpleBuffer::GetRawData(int64_t offset) { 52 | return nullptr; 53 | } 54 | 55 | bool SimpleBuffer::IsReadOnly() const { 56 | return false; 57 | } 58 | -------------------------------------------------------------------------------- /WTLHelper/CustomRebar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | 5 | class CCustomRebar : public CWindowImpl { 6 | public: 7 | void OnFinalMessage(HWND) override { 8 | delete this; 9 | } 10 | 11 | BEGIN_MSG_MAP(CCustomRebar) 12 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 13 | MESSAGE_HANDLER(WM_CTLCOLOREDIT, OnColorEdit) 14 | MESSAGE_HANDLER(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), OnUpdateTheme) 15 | END_MSG_MAP() 16 | 17 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& bHandled) { 18 | CDCHandle dc((HDC)wp); 19 | CRect rc; 20 | GetClientRect(&rc); 21 | dc.FillRect(&rc, ::GetSysColorBrush(COLOR_WINDOW)); 22 | return 1; 23 | } 24 | 25 | LRESULT OnColorEdit(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/) { 26 | auto theme = ThemeHelper::GetCurrentTheme(); 27 | 28 | CDCHandle dc((HDC)wp); 29 | dc.SetBkMode(OPAQUE); 30 | dc.SetTextColor(theme->TextColor); 31 | dc.SetBkColor(theme->BackColor); 32 | return (LRESULT)::GetSysColorBrush(COLOR_WINDOW); 33 | } 34 | 35 | LRESULT OnUpdateTheme(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/) { 36 | REBARBANDINFO info = { sizeof(info) }; 37 | info.fMask = RBBIM_COLORS; 38 | info.clrBack = ::GetSysColor(COLOR_WINDOW); 39 | info.clrFore = ::GetSysColor(COLOR_WINDOWTEXT); 40 | 41 | int count = GetBandCount(); 42 | for(int i = 0; i < count; i++) 43 | SetBandInfo(i, &info); 44 | return 0; 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /WTLHelper/VersionResourceHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include 3 | #include "VersionResourceHelper.h" 4 | 5 | #pragma comment(lib, "version") 6 | 7 | VersionResourceHelper::VersionResourceHelper(PCWSTR path) : m_path(path) { 8 | if (path == nullptr) { 9 | WCHAR exepath[MAX_PATH]; 10 | ::GetModuleFileName(nullptr, exepath, _countof(exepath)); 11 | m_path = exepath; 12 | } 13 | DWORD zero; 14 | auto infoSize = ::GetFileVersionInfoSize(m_path, &zero); 15 | if (infoSize) { 16 | m_buffer = std::make_unique(infoSize); 17 | if (!::GetFileVersionInfo(m_path, 0, infoSize, m_buffer.get())) 18 | m_buffer.reset(); 19 | } 20 | } 21 | 22 | VersionResourceHelper::VersionResourceHelper(PVOID data) : m_Data((BYTE const*)data) { 23 | } 24 | 25 | bool VersionResourceHelper::IsValid() const { 26 | return m_buffer != nullptr || m_Data != nullptr; 27 | } 28 | 29 | CString VersionResourceHelper::GetValue(const std::wstring& name) const { 30 | CString result; 31 | BYTE const* p = m_Data ? m_Data : m_buffer.get(); 32 | if (p) { 33 | WORD* langAndCodePage; 34 | UINT len; 35 | if (::VerQueryValue(p, L"\\VarFileInfo\\Translation", (void**)&langAndCodePage, &len)) { 36 | WCHAR text[256]; 37 | ::StringCchPrintf(text, _countof(text), L"\\StringFileInfo\\%04x%04x\\%s", langAndCodePage[0], langAndCodePage[1], name.c_str()); 38 | WCHAR* desc; 39 | if (::VerQueryValue(p, text, (void**)&desc, &len)) 40 | result = desc; 41 | } 42 | } 43 | return result; 44 | } 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /WTLHelper/Selection.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Selection.h" 3 | 4 | void Selection::SetSimple(int64_t offset, int64_t len) { 5 | m_Type = SelectionType::Simple; 6 | m_Offset = offset; 7 | m_Length = len; 8 | } 9 | 10 | void Selection::SetBox(int64_t offset, int bytesPerLine, int width, int height) { 11 | m_Type = SelectionType::Box; 12 | m_Offset = offset; 13 | m_Width = width; 14 | m_Height = height; 15 | m_BytesPerLine = bytesPerLine; 16 | } 17 | 18 | void Selection::SetAnchor(int64_t offset) { 19 | m_Anchor = offset; 20 | } 21 | 22 | int64_t Selection::GetOffset() const { 23 | return m_Offset; 24 | } 25 | 26 | int64_t Selection::GetAnchor() const { 27 | return m_Anchor; 28 | } 29 | 30 | bool Selection::IsSelected(int64_t offset) const { 31 | switch (m_Type) { 32 | case SelectionType::Simple: 33 | return offset >= m_Offset && offset < m_Offset + m_Length; 34 | 35 | case SelectionType::Box: 36 | if (offset < m_Offset) 37 | return false; 38 | return (offset - m_Offset) % m_BytesPerLine < m_Width && (offset - m_Offset) / m_BytesPerLine < m_Height; 39 | } 40 | ATLASSERT(false); 41 | return false; 42 | } 43 | 44 | bool Selection::IsEmpty() const { 45 | return m_Offset < 0; 46 | } 47 | 48 | SelectionType Selection::GetSelectionType() const { 49 | return m_Type; 50 | } 51 | 52 | int64_t Selection::GetLength() const { 53 | return m_Length; 54 | } 55 | 56 | void Selection::Clear() { 57 | m_Type = SelectionType::Simple; 58 | m_Offset = m_Anchor = -1; 59 | m_Length = 0; 60 | } 61 | -------------------------------------------------------------------------------- /WTLHelper/CompoundFileReaderWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CompoundFileReaderWriter.h" 3 | 4 | using namespace StructuredStorage; 5 | 6 | CompoundFileWriter::CompoundFileWriter(StructuredFile& file) : m_File(file) { 7 | } 8 | 9 | StructuredStorage::CompoundFileWriter::CompoundFileWriter(StructuredDirectory& dir, const std::wstring& name) 10 | : m_File(dir.CreateStructuredFile(name)) { 11 | } 12 | 13 | CompoundFileReader::CompoundFileReader(const StructuredFile& file) : m_File(file) { 14 | } 15 | 16 | void CompoundFileWriter::Write(const std::wstring& value) { 17 | auto len = static_cast(value.size()); 18 | m_File.Write(&len, sizeof(len)); 19 | m_File.Write(value.c_str(), len * sizeof(wchar_t)); 20 | } 21 | 22 | void CompoundFileReader::Read(std::wstring& value) const { 23 | uint32_t len; 24 | m_File.Read(&len, sizeof(len)); 25 | value.clear(); 26 | if (len) { 27 | auto buffer = std::make_unique(len); 28 | m_File.Read(buffer.get(), len * sizeof(WCHAR)); 29 | value.assign(buffer.get(), len); 30 | } 31 | } 32 | 33 | void CompoundFileWriter::Write(const std::string& value) { 34 | auto len = static_cast(value.size()); 35 | m_File.Write(&len, sizeof(len)); 36 | m_File.Write(value.c_str(), len * sizeof(char)); 37 | } 38 | 39 | void CompoundFileReader::Read(std::string& value) const { 40 | uint32_t len; 41 | m_File.Read(&len, sizeof(len)); 42 | auto buffer = std::make_unique(len); 43 | m_File.Read(buffer.get(), len); 44 | value.assign(buffer.get(), len); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /WTLHelper/CustomTabView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WM_WINDOW_MENU_BUILT (WM_APP+111) 4 | 5 | class CCustomTabView : public CTabViewImpl { 6 | public: 7 | DECLARE_WND_CLASS_EX(L"WTL_TabView", CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, COLOR_APPWORKSPACE) 8 | 9 | BEGIN_MSG_MAP(CCustomTabView) 10 | //MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 11 | MESSAGE_HANDLER(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), OnUpdateTheme) 12 | CHAIN_MSG_MAP(CTabViewImpl) 13 | ALT_MSG_MAP(1) 14 | CHAIN_MSG_MAP_ALT(CTabViewImpl, 1) 15 | END_MSG_MAP() 16 | 17 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) const; 18 | LRESULT OnUpdateTheme(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/); 19 | 20 | bool CreateTabControl(); 21 | void UpdateLayout(); 22 | void SetRedraw(bool redraw); 23 | void UpdateMenu(); 24 | void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false); 25 | 26 | bool m_Redraw{ true }; 27 | }; 28 | 29 | #define COMMAND_TABVIEW_HANDLER(tabs, msgMapId) \ 30 | if(uMsg == WM_COMMAND) { \ 31 | int page = tabs.GetActivePage(); \ 32 | if(page >= 0) { \ 33 | auto map = (CMessageMap*)tabs.GetPageData(page); \ 34 | if(map) { \ 35 | LRESULT result; \ 36 | if (map->ProcessWindowMessage(m_hWnd, uMsg, wParam, lParam, result, msgMapId)) \ 37 | return TRUE; \ 38 | } \ 39 | } \ 40 | } 41 | 42 | -------------------------------------------------------------------------------- /HexTest/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | // Change these values to use different versions 9 | #define WINVER 0x0601 10 | #define _WIN32_WINNT 0x0601 11 | #define _WIN32_IE 0x0700 12 | #define _RICHEDIT_VER 0x0500 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | extern CAppModule _Module; 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #if defined _M_IX86 31 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 32 | #elif defined _M_IA64 33 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 34 | #elif defined _M_X64 35 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 36 | #else 37 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 38 | #endif 39 | -------------------------------------------------------------------------------- /WTLHelper/CustomDialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | 5 | class CCustomDialog : public CWindowImpl { 6 | public: 7 | BEGIN_MSG_MAP(CCustomDialog) 8 | MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnStaticColor) 9 | MESSAGE_HANDLER(WM_CTLCOLORDLG, OnDialogColor) 10 | MESSAGE_HANDLER(WM_CTLCOLORLISTBOX, OnStaticColor) 11 | MESSAGE_HANDLER(WM_CTLCOLORSCROLLBAR, OnScrollBarColor) 12 | MESSAGE_HANDLER(WM_CTLCOLOREDIT, OnEditColor) 13 | END_MSG_MAP() 14 | 15 | LRESULT OnScrollBarColor(UINT, WPARAM wp, LPARAM lp, BOOL&) { 16 | return (LRESULT)::GetSysColorBrush(COLOR_WINDOW); 17 | } 18 | 19 | LRESULT OnDialogColor(UINT, WPARAM wp, LPARAM lp, BOOL&) { 20 | auto theme = ThemeHelper::GetCurrentTheme(); 21 | ATLASSERT(theme); 22 | 23 | CDCHandle dc((HDC)wp); 24 | dc.SetBkMode(OPAQUE); 25 | dc.SetTextColor(theme->TextColor); 26 | dc.SetBkColor(theme->BackColor); 27 | return (LRESULT)::GetSysColorBrush(COLOR_WINDOW); 28 | } 29 | 30 | LRESULT OnStaticColor(UINT, WPARAM wp, LPARAM lp, BOOL& handled) { 31 | auto theme = ThemeHelper::GetCurrentTheme(); 32 | ATLASSERT(theme); 33 | 34 | CDCHandle dc((HDC)wp); 35 | dc.SetBkMode(OPAQUE); 36 | dc.SetTextColor(theme->TextColor); 37 | dc.SetBkColor(theme->BackColor); 38 | return (LRESULT)theme->GetSysBrush(COLOR_WINDOW); 39 | } 40 | 41 | LRESULT OnEditColor(UINT, WPARAM wp, LPARAM lp, BOOL&) { 42 | auto theme = ThemeHelper::GetCurrentTheme(); 43 | ATLASSERT(theme); 44 | 45 | CDCHandle dc((HDC)wp); 46 | dc.SetBkMode(OPAQUE); 47 | dc.SetTextColor(theme->TextColor); 48 | dc.SetBkColor(theme->BackColor); 49 | return (LRESULT)theme->GetSysBrush(COLOR_WINDOW); 50 | } 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /BasicDemo/pch.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | // Change these values to use different versions 9 | #define WINVER 0x0601 10 | #define _WIN32_WINNT 0x0601 11 | #define _WIN32_IE 0x0700 12 | #define _RICHEDIT_VER 0x0500 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | extern CAppModule _Module; 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined _M_IX86 34 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 35 | #elif defined _M_IA64 36 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 37 | #elif defined _M_X64 38 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 39 | #else 40 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 41 | #endif 42 | -------------------------------------------------------------------------------- /WTLHelper/CustomToolBar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CCustomToolBar : public CWindowImpl { 4 | public: 5 | void OnFinalMessage(HWND) override { 6 | } 7 | 8 | BEGIN_MSG_MAP(CCustomRebar) 9 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 10 | // MESSAGE_HANDLER(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), OnUpdateTheme) 11 | END_MSG_MAP() 12 | 13 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& bHandled) { 14 | CDCHandle dc((HDC)wp); 15 | CRect rc; 16 | GetClientRect(&rc); 17 | dc.FillRect(&rc, ::GetSysColorBrush(COLOR_WINDOW)); 18 | return 1; 19 | } 20 | 21 | }; 22 | 23 | class CCustomToolBarParent : 24 | public CWindowImpl, 25 | public CCustomDraw { 26 | 27 | BEGIN_MSG_MAP(CCustomToolBarParent) 28 | CHAIN_MSG_MAP(CCustomDraw) 29 | END_MSG_MAP() 30 | 31 | DWORD OnPrePaint(int, LPNMCUSTOMDRAW cd) { 32 | if (cd->hdr.hwndFrom != m_ToolBar) { 33 | SetMsgHandled(FALSE); 34 | return CDRF_DODEFAULT; 35 | } 36 | return CDRF_NOTIFYITEMDRAW; 37 | } 38 | 39 | DWORD OnItemPrePaint(int, LPNMCUSTOMDRAW cd) { 40 | if (cd->hdr.hwndFrom != m_ToolBar) { 41 | SetMsgHandled(FALSE); 42 | return CDRF_DODEFAULT; 43 | } 44 | auto tb = (NMTBCUSTOMDRAW*)cd; 45 | tb->clrText = ThemeHelper::GetCurrentTheme()->TextColor; 46 | tb->clrBtnHighlight = ::GetSysColor(COLOR_BTNHIGHLIGHT); 47 | 48 | return TBCDRF_USECDCOLORS; 49 | } 50 | 51 | void OnFinalMessage(HWND) override { 52 | delete this; 53 | } 54 | 55 | void Init(HWND tb) { 56 | SubclassWindow(::GetParent(tb)); 57 | m_ToolBar.Attach(tb); 58 | } 59 | 60 | CToolBarCtrl m_ToolBar; 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /HexTest/View.h: -------------------------------------------------------------------------------- 1 | // View.h : interface of the CView class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | class SimpleBuffer : public IBufferManager { 11 | // Inherited via IBufferManager 12 | virtual uint32_t GetData(int64_t offset, uint8_t* buffer, uint32_t count) override; 13 | virtual bool Insert(int64_t offset, const uint8_t* data, uint32_t count) override; 14 | virtual bool Delete(int64_t offset, size_t count) override; 15 | virtual bool SetData(int64_t offset, const uint8_t* data, uint32_t count) override; 16 | virtual int64_t GetSize() const override; 17 | virtual uint8_t* GetRawData(int64_t offset) override; 18 | virtual bool IsReadOnly() const override; 19 | }; 20 | 21 | class CView : public CWindowImpl { 22 | public: 23 | DECLARE_WND_CLASS(NULL) 24 | 25 | BOOL PreTranslateMessage(MSG* pMsg); 26 | 27 | BEGIN_MSG_MAP(CView) 28 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 29 | MESSAGE_HANDLER(WM_SIZE, OnSize) 30 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 31 | END_MSG_MAP() 32 | 33 | // Handler prototypes (uncomment arguments if needed): 34 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 35 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 36 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 37 | 38 | LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 39 | LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 40 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 41 | 42 | CHexControl m_Hex; 43 | SimpleBuffer m_buffer; 44 | }; 45 | -------------------------------------------------------------------------------- /HexTest/MainFrm.h: -------------------------------------------------------------------------------- 1 | // MainFrm.h : interface of the CMainFrame class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | class CMainFrame : 10 | public CFrameWindowImpl, 11 | public CAutoUpdateUI, 12 | public CMessageFilter, 13 | public CIdleHandler { 14 | public: 15 | using BaseFrame = CFrameWindowImpl; 16 | DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME) 17 | 18 | CView m_view; 19 | 20 | virtual BOOL PreTranslateMessage(MSG* pMsg); 21 | virtual BOOL OnIdle(); 22 | 23 | BEGIN_UPDATE_UI_MAP(CMainFrame) 24 | END_UPDATE_UI_MAP() 25 | 26 | BEGIN_MSG_MAP(CMainFrame) 27 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 28 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 29 | COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) 30 | COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) 31 | COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) 32 | CHAIN_MSG_MAP(CAutoUpdateUI) 33 | CHAIN_MSG_MAP(BaseFrame) 34 | END_MSG_MAP() 35 | 36 | // Handler prototypes (uncomment arguments if needed): 37 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 38 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 39 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 40 | 41 | LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 42 | LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); 43 | LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 44 | LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 45 | LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 46 | }; 47 | -------------------------------------------------------------------------------- /WTLHelper.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32422.2 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WTLHelper", "WTLHelper\WTLHelper.vcxproj", "{AE53419F-A769-4548-8E15-E311904DF7DF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM64 = Debug|ARM64 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|ARM64 = Release|ARM64 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|ARM64.ActiveCfg = Debug|ARM64 19 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|ARM64.Build.0 = Debug|ARM64 20 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x64.ActiveCfg = Debug|x64 21 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x64.Build.0 = Debug|x64 22 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x86.ActiveCfg = Debug|Win32 23 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Debug|x86.Build.0 = Debug|Win32 24 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|ARM64.ActiveCfg = Release|ARM64 25 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|ARM64.Build.0 = Release|ARM64 26 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x64.ActiveCfg = Release|x64 27 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x64.Build.0 = Release|x64 28 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x86.ActiveCfg = Release|Win32 29 | {AE53419F-A769-4548-8E15-E311904DF7DF}.Release|x86.Build.0 = Release|Win32 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {8653165E-01AB-4E6E-BE88-D83A8F046332} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /WTLHelper/SortHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "SortHelper.h" 3 | 4 | bool SortHelper::Sort(const CString& s1, const CString& s2, bool ascending) { 5 | if (s1.IsEmpty() && s2.IsEmpty()) 6 | return false; 7 | if (s1.IsEmpty()) 8 | return false; 9 | if (s2.IsEmpty()) 10 | return true; 11 | 12 | return ascending ? s2.CompareNoCase(s1) > 0 : s2.CompareNoCase(s1) < 0; 13 | } 14 | 15 | bool SortHelper::Sort(const std::string& s1, const std::string& s2, bool ascending) { 16 | if (s1.empty() && s2.empty()) 17 | return false; 18 | if (s1.empty()) 19 | return false; 20 | if (s2.empty()) 21 | return true; 22 | 23 | auto compare = ::_stricmp(s2.c_str(), s1.c_str()); 24 | return ascending ? compare > 0 : compare < 0; 25 | } 26 | 27 | bool SortHelper::Sort(const std::wstring& s1, const std::wstring& s2, bool ascending) { 28 | if (s1.empty() && s2.empty()) 29 | return false; 30 | if (s1.empty()) 31 | return false; 32 | if (s2.empty()) 33 | return true; 34 | 35 | auto compare = ::_wcsicmp(s2.c_str(), s1.c_str()); 36 | return ascending ? compare > 0 : compare < 0; 37 | } 38 | 39 | bool SortHelper::Sort(PCWSTR s1, PCWSTR s2, bool ascending) { 40 | if ((s1 == nullptr || *s1 == 0) && (s2 == nullptr || *s2 == 0)) 41 | return false; 42 | if (s1 == nullptr || *s1 == 0) 43 | return false; 44 | if (s2 == nullptr || *s2 == 0) 45 | return true; 46 | 47 | auto compare = ::_wcsicmp(s2, s1); 48 | return ascending ? compare > 0 : compare < 0; 49 | } 50 | 51 | bool SortHelper::Sort(PWSTR s1, PWSTR s2, bool ascending) { 52 | if ((s1 == nullptr || *s1 == 0) && (s2 == nullptr || *s2 == 0)) 53 | return false; 54 | if (s1 == nullptr || *s1 == 0) 55 | return false; 56 | if (s2 == nullptr || *s2 == 0) 57 | return true; 58 | 59 | auto compare = ::_wcsicmp(s2, s1); 60 | return ascending ? compare > 0 : compare < 0; 61 | } 62 | 63 | bool SortHelper::Sort(bool a, bool b, bool asc) { 64 | return asc ? b > a : a > b; 65 | } 66 | -------------------------------------------------------------------------------- /HexTest/MainFrm.cpp: -------------------------------------------------------------------------------- 1 | // MainFrm.cpp : implmentation of the CMainFrame class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "stdafx.h" 6 | #include "resource.h" 7 | 8 | #include "aboutdlg.h" 9 | #include "View.h" 10 | #include "MainFrm.h" 11 | 12 | BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { 13 | if (BaseFrame::PreTranslateMessage(pMsg)) 14 | return TRUE; 15 | 16 | return m_view.PreTranslateMessage(pMsg); 17 | } 18 | 19 | BOOL CMainFrame::OnIdle() { 20 | return FALSE; 21 | } 22 | 23 | LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { 24 | 25 | m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE); 26 | 27 | // register object for message filtering and idle updates 28 | CMessageLoop* pLoop = _Module.GetMessageLoop(); 29 | ATLASSERT(pLoop != NULL); 30 | pLoop->AddMessageFilter(this); 31 | pLoop->AddIdleHandler(this); 32 | 33 | return 0; 34 | } 35 | 36 | LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { 37 | // unregister message filtering and idle updates 38 | CMessageLoop* pLoop = _Module.GetMessageLoop(); 39 | ATLASSERT(pLoop != NULL); 40 | pLoop->RemoveMessageFilter(this); 41 | pLoop->RemoveIdleHandler(this); 42 | 43 | bHandled = FALSE; 44 | return 1; 45 | } 46 | 47 | LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 48 | PostMessage(WM_CLOSE); 49 | return 0; 50 | } 51 | 52 | LRESULT CMainFrame::OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 53 | // TODO: add code to initialize document 54 | 55 | return 0; 56 | } 57 | 58 | LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 59 | CAboutDlg dlg; 60 | dlg.DoModal(); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /WTLHelper/CustomButton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma once 4 | 5 | #include "ThemeHelper.h" 6 | 7 | class CCustomButtonParent : 8 | public CWindowImpl, 9 | public CCustomDraw { 10 | public: 11 | void OnFinalMessage(HWND) override { 12 | m_Button.Detach(); 13 | delete this; 14 | } 15 | 16 | BEGIN_MSG_MAP(CCustomButtonParent) 17 | CHAIN_MSG_MAP(CCustomDraw) 18 | END_MSG_MAP() 19 | 20 | DWORD OnPrePaint(int, LPNMCUSTOMDRAW cd) { 21 | if (cd->hdr.hwndFrom != m_Button) { 22 | SetMsgHandled(FALSE); 23 | return CDRF_DODEFAULT; 24 | } 25 | return CDRF_NOTIFYPOSTERASE; 26 | } 27 | 28 | DWORD OnItemPrePaint(int, LPNMCUSTOMDRAW cd) { 29 | if (cd->hdr.hwndFrom != m_Button) { 30 | SetMsgHandled(FALSE); 31 | return CDRF_DODEFAULT; 32 | } 33 | return CDRF_NOTIFYPOSTERASE; 34 | } 35 | 36 | DWORD OnSubItemPrePaint(int, LPNMCUSTOMDRAW cd) { 37 | SetMsgHandled(FALSE); 38 | return CDRF_DODEFAULT; 39 | } 40 | 41 | DWORD OnPreErase(int, LPNMCUSTOMDRAW cd) { 42 | if (cd->hdr.hwndFrom != m_Button) { 43 | SetMsgHandled(FALSE); 44 | return CDRF_DODEFAULT; 45 | } 46 | return CDRF_NOTIFYPOSTERASE; 47 | } 48 | 49 | DWORD OnPostErase(int, LPNMCUSTOMDRAW cd) { 50 | if (cd->hdr.hwndFrom != m_Button) { 51 | SetMsgHandled(FALSE); 52 | return CDRF_DODEFAULT; 53 | } 54 | CDCHandle dc(cd->hdc); 55 | auto theme = ThemeHelper::GetCurrentTheme(); 56 | dc.FillSolidRect(&cd->rc, (cd->uItemState & (CDIS_DISABLED | CDIS_GRAYED)) ? ::GetSysColor(COLOR_GRAYTEXT) : theme->BackColor); 57 | dc.DrawEdge(&cd->rc, (cd->uItemState & CDIS_SELECTED) ? EDGE_SUNKEN : EDGE_BUMP, BF_RECT); 58 | dc.SetBkMode(TRANSPARENT); 59 | dc.SetTextColor(theme->TextColor); 60 | return CDRF_DODEFAULT; 61 | } 62 | 63 | void Init(HWND hWnd) { 64 | m_Button.Attach(hWnd); 65 | m_Button.ModifyStyleEx(WS_EX_NOPARENTNOTIFY, 0); 66 | ATLVERIFY(SubclassWindow(::GetParent(hWnd))); 67 | } 68 | 69 | CButton m_Button; 70 | }; 71 | 72 | -------------------------------------------------------------------------------- /WTLHelper/DialogHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class CDialogHelper { 5 | public: 6 | template 7 | void InitCombo(UINT id, S (&data)[N], U const& selected = U()) { 8 | InitCombo(id, data, N, selected); 9 | } 10 | 11 | template 12 | void InitCombo(UINT id, S const* data, int count, U const& selected = U()) { 13 | auto dlg = static_cast(this); 14 | auto cb = (CComboBox)dlg->GetDlgItem(id); 15 | int cur = -1; 16 | for (int i = 0; i < count; i++) { 17 | auto& item = data[i]; 18 | int n = cb.AddString(item.text); 19 | cb.SetItemData(n, static_cast(item.type)); 20 | if (cur < 0 && item.type == selected) { 21 | cur = n; 22 | } 23 | } 24 | cb.SetCurSel(cur >= 0 ? cur : 0); 25 | } 26 | 27 | void AdjustOKCancelButtons(UINT okId, UINT cancelId) { 28 | auto dlg = static_cast(this); 29 | CButton ok(dlg->GetDlgItem(IDOK)); 30 | if (ok) { 31 | CString text; 32 | ok.GetWindowText(text); 33 | ok.SetWindowText(L" " + text); 34 | ok.SetIcon(AtlLoadIconImage(okId, 0, 16, 16)); 35 | } 36 | 37 | CButton cancel(dlg->GetDlgItem(IDCANCEL)); 38 | if (cancel) { 39 | cancel.SetWindowText(L" Cancel"); 40 | cancel.SetIcon(AtlLoadIconImage(cancelId, 0, 16, 16)); 41 | } 42 | } 43 | 44 | bool AddIconToButton(WORD id, WORD icon, int size = 16) { 45 | auto dlg = static_cast(this); 46 | CButton button(dlg->GetDlgItem(id)); 47 | if (button) { 48 | button.SetIcon(AtlLoadIconImage(icon, 0, size, size)); 49 | CString text; 50 | button.GetWindowText(text); 51 | button.SetWindowText(L" " + text); 52 | } 53 | return (bool)button; 54 | } 55 | 56 | void SetDialogIcon(UINT icon) { 57 | auto dlg = static_cast(this); 58 | dlg->SetIcon(AtlLoadIconImage(icon, 0, 16, 16), FALSE); 59 | dlg->SetIcon(AtlLoadIconImage(icon, 0, 32, 32), TRUE); 60 | } 61 | void SetDialogIcon(HICON icon) { 62 | auto dlg = static_cast(this); 63 | dlg->SetIcon(icon, FALSE); 64 | dlg->SetIcon(icon, TRUE); 65 | } 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /WTLHelper/IListView.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "IListView.h" 3 | 4 | int IListView::GetSelectedIndex() const { 5 | int selected = -1; 6 | GetSelectedCount(&selected); 7 | if (selected != 1) 8 | return -1; 9 | LVITEMINDEX idx; 10 | return GetNextItem(LVITEMINDEX{ -1, -1 }, LVNI_SELECTED, &idx) == S_OK ? idx.iItem : -1; 11 | } 12 | 13 | int IListView::GetItemCount() const { 14 | int count; 15 | return S_OK == GetItemCount(&count) ? count : -1; 16 | } 17 | 18 | int IListView::GetTopIndex() const { 19 | int top; 20 | return GetTopIndex(&top) == S_OK ? top : -1; 21 | } 22 | 23 | int IListView::GetCountPerPage() const { 24 | int count; 25 | return GetCountPerPage(&count) == S_OK ? count : -1; 26 | } 27 | 28 | bool IListView::IsItemVisible(int index) const { 29 | BOOL visible{ false }; 30 | IsItemVisible(LVITEMINDEX{ index, -1 }, &visible); 31 | return visible; 32 | } 33 | 34 | CString IListView::GetItemText(int row, int column) const { 35 | CString text; 36 | GetItemText(row, column, text.GetBufferSetLength(256), 256); 37 | text.FreeExtra(); 38 | return text; 39 | } 40 | 41 | ULONG IListView::GetItemState(int row, ULONG mask) const { 42 | ULONG state; 43 | return S_OK == GetItemState(row, 0, mask, &state) ? state : 0; 44 | } 45 | 46 | int IListView::GetNextItem(int item, ULONG mask) const { 47 | LVITEMINDEX next{ -1, -1 }; 48 | GetNextItem(LVITEMINDEX{ item, -1 }, mask, &next); 49 | return next.iItem; 50 | } 51 | 52 | int IListView::GetSelectedCount() const { 53 | int count; 54 | return S_OK == GetSelectedCount(&count) ? count : 0; 55 | } 56 | 57 | UINT IListView::GetHoverTime() const { 58 | UINT hover; 59 | return S_OK == GetHoverTime(&hover) ? hover : 0; 60 | } 61 | 62 | int IListView::GetColumnWidth(int columnIndex) const { 63 | int width; 64 | return S_OK == GetColumnWidth(columnIndex, &width) ? width : -1; 65 | } 66 | 67 | HWND IListView::GetHeader() const { 68 | HWND h; 69 | return S_OK == GetHeaderControl(&h) ? h : nullptr; 70 | } 71 | -------------------------------------------------------------------------------- /BasicDemo/ProcessTreeListView.h: -------------------------------------------------------------------------------- 1 | // View.h : interface of the CView class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | #include "TreeListViewCtrl.h" 8 | 9 | class CProcessTreeListView : 10 | public CFrameWindowImpl { 11 | using BaseFrame = CFrameWindowImpl; 12 | public: 13 | 14 | BOOL PreTranslateMessage(MSG* pMsg); 15 | 16 | virtual void OnFinalMessage(HWND /*hWnd*/); 17 | 18 | protected: 19 | BEGIN_MSG_MAP(CProcessTreeListView) 20 | NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDED, OnItemChanged) 21 | NOTIFY_CODE_HANDLER(TLN_GETDISPINFO, OnTreeListGetDispInfo) 22 | NOTIFY_CODE_HANDLER(TVN_GETDISPINFO, OnTreeGetDispInfo) 23 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 24 | CHAIN_MSG_MAP(BaseFrame) 25 | REFLECT_NOTIFICATIONS_EX() 26 | END_MSG_MAP() 27 | 28 | private: 29 | struct ProcessInfo { 30 | DWORD Id; 31 | CString Name; 32 | DWORD Threads; 33 | }; 34 | 35 | void Refresh(); 36 | 37 | // Handler prototypes (uncomment arguments if needed): 38 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 39 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 40 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 41 | 42 | LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 43 | LRESULT OnItemChanged(int /*idCtrl*/, LPNMHDR /*nymph*/, BOOL& /*bHandled*/); 44 | LRESULT OnTreeGetDispInfo(int /*idCtrl*/, LPNMHDR /*nymph*/, BOOL& /*bHandled*/); 45 | LRESULT OnTreeListGetDispInfo(int /*idCtrl*/, LPNMHDR /*nymph*/, BOOL& /*bHandled*/); 46 | LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/); 47 | 48 | std::unordered_map m_Processes; 49 | 50 | CTreeListView m_TreeList; 51 | int m_SortColumn{ -1 }; 52 | int m_HighlightDiff{ 2000 }; 53 | bool m_SortAscending{ true }; 54 | bool m_TrackSelectedItem{ false }; 55 | bool m_ScrollToNewProcesses{ false }; 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /HexTest/HexTest.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {8ae562a7-2965-4d6b-9ab0-227702a794ba} 6 | cpp;c;cxx;def;odl;idl;hpj;bat;asm 7 | 8 | 9 | {33db01d1-df44-4bdb-baac-9826c2a3e29e} 10 | h;hpp;hxx;hm;inl;inc 11 | 12 | 13 | {946bf415-a01e-4901-8a26-cafbf5e1fa29} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | 52 | 53 | Resource Files 54 | 55 | 56 | 57 | 58 | Resource Files 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /BasicDemo/BasicDemo.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {26dc02f3-3874-4f34-9ad2-e6aee259f257} 6 | cpp;c;cxx;def;odl;idl;hpj;bat;asm 7 | 8 | 9 | {687316eb-113a-4f37-8e5b-28459c9ab0d2} 10 | h;hpp;hxx;hm;inl;inc 11 | 12 | 13 | {ad22ce6f-49af-402e-acf1-7322aedb69ba} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | 52 | 53 | Resource Files 54 | 55 | 56 | 57 | 58 | Resource Files 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /WTLHelper/CustomTabControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CCustomTabControl : public CWindowImpl { 4 | public: 5 | BEGIN_MSG_MAP(CCustomTabContro) 6 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 7 | END_MSG_MAP() 8 | 9 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 10 | CDCHandle dc((HDC)wParam); 11 | CRect rc; 12 | GetClientRect(&rc); 13 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 14 | return 1; 15 | } 16 | 17 | }; 18 | 19 | struct CCustomTabControlParent : 20 | CWindowImpl, 21 | COwnerDraw { 22 | BEGIN_MSG_MAP(CCustomTabControlParent) 23 | CHAIN_MSG_MAP(COwnerDraw) 24 | END_MSG_MAP() 25 | 26 | void OnFinalMessage(HWND) override { 27 | delete this; 28 | } 29 | 30 | void MeasureItem(LPMEASUREITEMSTRUCT mis) { 31 | if (mis->CtlType != ODT_TAB) { 32 | SetMsgHandled(FALSE); 33 | return; 34 | } 35 | // not called with a dialog box 36 | mis->itemWidth = 120; 37 | } 38 | 39 | void DrawItem(LPDRAWITEMSTRUCT dis) { 40 | if (dis->hwndItem != m_Tab) { 41 | SetMsgHandled(FALSE); 42 | return; 43 | } 44 | CDCHandle dc(dis->hDC); 45 | auto& rc(dis->rcItem); 46 | auto theme = ThemeHelper::GetCurrentTheme(); 47 | TCITEM tci; 48 | WCHAR text[32]; 49 | tci.mask = TCIF_STATE | TCIF_TEXT | TCIF_IMAGE; 50 | tci.dwStateMask = TCIS_HIGHLIGHTED; 51 | tci.pszText = text; 52 | tci.cchTextMax = _countof(text); 53 | ATLVERIFY(m_Tab.GetItem(dis->itemID, &tci)); 54 | 55 | dc.SelectFont(m_Tab.GetFont()); 56 | dc.FillSolidRect(&rc, theme->BackColor); 57 | dc.SetTextColor(theme->TextColor); 58 | dc.SetBkMode(TRANSPARENT); 59 | if (tci.iImage >= 0) { 60 | CSize size; 61 | ::GetTextExtentPoint32(dc.m_hDC, tci.pszText, (int)wcslen(tci.pszText), &size); 62 | CRect irc(rc); 63 | irc.top += 4; 64 | irc.bottom = irc.top + 16; 65 | irc.left += 4; 66 | irc.right = irc.left + 16; 67 | 68 | m_Tab.GetImageList().DrawEx(tci.iImage, dc.m_hDC, irc, CLR_NONE, CLR_NONE, ILD_NORMAL); 69 | rc.left += 24; 70 | rc.top += 2; 71 | } 72 | else { 73 | rc.left += 4; 74 | } 75 | dc.DrawText(tci.pszText, -1, &rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE); 76 | } 77 | 78 | void Init(HWND hWnd) { 79 | m_Tab.SubclassWindow(hWnd); 80 | m_Tab.ModifyStyle(0, TCS_OWNERDRAWFIXED); 81 | } 82 | 83 | CCustomTabControl m_Tab; 84 | }; 85 | 86 | -------------------------------------------------------------------------------- /WTLHelper/CustomSplitterWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | #include "Theme.h" 5 | #include 6 | 7 | template 8 | class CCustomSplitterWindowT : public CSplitterWindowImpl> { 9 | public: 10 | DECLARE_WND_CLASS2(_T("WTL_SplitterWindow"), CSplitterWindowT) 11 | 12 | BEGIN_MSG_MAP(CCustomSplitterWindowT) 13 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 14 | CHAIN_MSG_MAP(CSplitterWindowImpl>) 15 | END_MSG_MAP() 16 | 17 | CCustomSplitterWindowT() : CSplitterWindowImpl>(t_bVertical) { 18 | this->m_dwExtendedStyle |= SPLIT_FIXEDBARSIZE; 19 | } 20 | 21 | LRESULT OnCreate(UINT, WPARAM wp, LPARAM lp, BOOL& handled) { 22 | auto lpcs = (LPCREATESTRUCT)lp; 23 | this->m_cxySplitBar = (lpcs->dwExStyle & WS_EX_CLIENTEDGE) ? 2 : 6; 24 | handled = FALSE; 25 | return 0; 26 | } 27 | 28 | void DrawSplitterBar(CDCHandle dc) { 29 | RECT rect = {}; 30 | if (this->GetSplitterBarRect(&rect)) { 31 | dc.FillRect(&rect, COLOR_3DFACE); 32 | 33 | if ((this->m_dwExtendedStyle & SPLIT_FLATBAR) != 0) { 34 | RECT rect1 = rect; 35 | if (t_bVertical) 36 | rect1.right = rect1.left + 1; 37 | else 38 | rect1.bottom = rect1.top + 1; 39 | dc.FillRect(&rect1, COLOR_WINDOW); 40 | 41 | rect1 = rect; 42 | if (t_bVertical) 43 | rect1.left = rect1.right - 1; 44 | else 45 | rect1.top = rect1.bottom - 1; 46 | dc.FillRect(&rect1, COLOR_3DSHADOW); 47 | } 48 | else if ((this->m_dwExtendedStyle & SPLIT_GRADIENTBAR) != 0) { 49 | RECT rect2 = rect; 50 | if (t_bVertical) 51 | rect2.left = (rect.left + rect.right) / 2 - 1; 52 | else 53 | rect2.top = (rect.top + rect.bottom) / 2 - 1; 54 | 55 | dc.GradientFillRect(rect2, ::GetSysColor(COLOR_3DFACE), ::GetSysColor(COLOR_3DSHADOW), t_bVertical); 56 | } 57 | 58 | //dc.DrawEdge(&rect, EDGE_ETCHED, t_bVertical ? (BF_LEFT | BF_RIGHT) : (BF_TOP | BF_BOTTOM)); 59 | dc.FillRect(&rect, ::GetSysColorBrush(COLOR_WINDOW)); 60 | if constexpr (t_bVertical) { 61 | rect.top--; 62 | rect.bottom++; 63 | } 64 | else { 65 | rect.left--; 66 | rect.right++; 67 | } 68 | dc.FrameRect(&rect, ::GetSysColorBrush(COLOR_GRAYTEXT)); 69 | } 70 | } 71 | 72 | }; 73 | 74 | using CCustomSplitterWindow = CCustomSplitterWindowT; 75 | using CCustomHorSplitterWindow = CCustomSplitterWindowT; 76 | -------------------------------------------------------------------------------- /BasicDemo/MainFrm.h: -------------------------------------------------------------------------------- 1 | // MainFrm.h : interface of the CMainFrame class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | class CMainFrame : 10 | public CFrameWindowImpl, 11 | public CAutoUpdateUI, 12 | public CMessageFilter, public CIdleHandler { 13 | public: 14 | DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME) 15 | 16 | virtual BOOL PreTranslateMessage(MSG* pMsg); 17 | virtual BOOL OnIdle(); 18 | 19 | protected: 20 | BEGIN_MSG_MAP(CMainFrame) 21 | COMMAND_ID_HANDLER(ID_DEMO_TREELISTVIEW, OnTreeListDemo) 22 | COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar) 23 | COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) 24 | COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose) 25 | COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll) 26 | COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate) 27 | COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) 28 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 29 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 30 | CHAIN_MSG_MAP(CAutoUpdateUI) 31 | CHAIN_MSG_MAP(CFrameWindowImpl) 32 | REFLECT_NOTIFICATIONS_EX() 33 | END_MSG_MAP() 34 | 35 | private: 36 | // Handler prototypes (uncomment arguments if needed): 37 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 38 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 39 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 40 | 41 | LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 42 | LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); 43 | LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 44 | LRESULT OnTreeListDemo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 45 | LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 46 | LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 47 | LRESULT OnWindowClose(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 48 | LRESULT OnWindowCloseAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 49 | LRESULT OnWindowActivate(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/); 50 | 51 | CTabView m_view; 52 | }; 53 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /WTLHelper/ListViewhelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct IListView; 4 | struct ColumnsState; 5 | 6 | struct ListViewHelper abstract final { 7 | static bool SaveAll(PCWSTR path, CListViewCtrl& lv, PCWSTR separator = L",", bool includeHeaders = true); 8 | static bool SaveAllToKey(CRegKey& key, CListViewCtrl& lv, bool includeHeaders = true); 9 | static CString GetRowAsString(CListViewCtrl const& lv, int row, PCWSTR separator = L"\t"); 10 | static CString GetSelectedRowsAsString(CListViewCtrl const& lv, PCWSTR separator = L"\t", PCWSTR cr = L"\r\n"); 11 | static int FindItem(CListViewCtrl const& lv, PCWSTR text, bool partial); 12 | static int SearchItem(CListViewCtrl const& lv, PCWSTR text, bool down, bool caseSenstive); 13 | 14 | static int FindRow(CListViewCtrl const& lv, PCWSTR rowText, int start = -1); 15 | static int FindRow(CListViewCtrl const& lv, int colStart, int count, PCWSTR rowText, int start = -1); 16 | static IListView* GetIListView(HWND hListView); 17 | static CString GetRowColumnsAsString(CListViewCtrl const& lv, int row, int start, int count, PCWSTR separator = L"\t"); 18 | static CString GetAllRowsAsString(CListViewCtrl const& lv, PCWSTR separator = L"\t", PCWSTR cr = L"\r\n"); 19 | static bool WriteColumnsState(ColumnsState const& state, IStream* stm); 20 | static bool ReadColumnsState(ColumnsState& state, IStream* stm); 21 | }; 22 | 23 | struct SelectedItemsView : std::ranges::view_interface { 24 | struct Iterator { 25 | Iterator(SelectedItemsView const& view, bool end = false) : _view(view), _end(end) { 26 | if(!end) 27 | _index = view._lv.GetNextItem(-1, LVNI_SELECTED); 28 | } 29 | int operator*() const { 30 | return _index; 31 | } 32 | int operator++(int) { 33 | auto index = _index; 34 | _index = _view._lv.GetNextItem(_index, LVNI_SELECTED); 35 | if (_index == -1) 36 | _end = true; 37 | return index; 38 | } 39 | int operator++() { 40 | _index = _view._lv.GetNextItem(_index, LVNI_SELECTED); 41 | if (_index == -1) 42 | _end = true; 43 | return _index; 44 | } 45 | bool operator==(Iterator other) const { 46 | if (_end && other._end) 47 | return true; 48 | return false; 49 | } 50 | bool operator!=(Iterator other) const { 51 | return !(*this == other); 52 | } 53 | 54 | int _index{ -1 }; 55 | SelectedItemsView const& _view; 56 | bool _end; 57 | }; 58 | 59 | SelectedItemsView() = default; 60 | explicit SelectedItemsView(CListViewCtrl const& lv) : _lv(lv), _count(lv.GetSelectedCount()) {} 61 | 62 | Iterator begin() { 63 | return Iterator(*this); 64 | } 65 | Iterator end() { 66 | return Iterator(*this, true); 67 | } 68 | 69 | Iterator const begin() const { 70 | return Iterator(*this); 71 | } 72 | Iterator const end() const { 73 | return Iterator(*this, true); 74 | } 75 | 76 | size_t size() const { 77 | return _count; 78 | } 79 | 80 | private: 81 | CListViewCtrl const& _lv; 82 | int _count; 83 | }; 84 | -------------------------------------------------------------------------------- /WTLHelper/CompoundFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace StructuredStorage { 8 | enum class CompoundFileMode { 9 | Read, 10 | ReadWrite 11 | }; 12 | 13 | enum class SeekMode { 14 | Set, 15 | Current, 16 | End 17 | }; 18 | 19 | class StructuredFile { 20 | friend class StructuredDirectory; 21 | 22 | public: 23 | bool IsValid() const { 24 | return m_spStream != nullptr; 25 | } 26 | 27 | operator bool() const { 28 | return IsValid(); 29 | } 30 | 31 | HRESULT Write(const void* buffer, uint32_t count); 32 | HRESULT Read(void* buffer, uint32_t count) const; 33 | 34 | HRESULT Seek(uint32_t offset, SeekMode mode = SeekMode::Set, uint32_t* newOffset = nullptr); 35 | 36 | uint32_t GetSize() const; 37 | std::wstring GetName() const; 38 | 39 | void Close(); 40 | 41 | operator IStream* () const { 42 | return m_spStream.p; 43 | } 44 | 45 | private: 46 | explicit StructuredFile(IStream* pStm) : m_spStream(pStm) {} 47 | 48 | CComPtr m_spStream; 49 | }; 50 | 51 | class StructuredDirectory { 52 | public: 53 | StructuredDirectory CreateStructuredDirectory(const std::wstring& name); 54 | StructuredFile CreateStructuredFile(const std::wstring& name); 55 | StructuredDirectory OpenStructuredDirectory(const std::wstring& name) const; 56 | StructuredFile OpenStructuredFile(const std::wstring& name) const; 57 | 58 | bool IsValid() const { 59 | return m_spStorage.p; 60 | } 61 | 62 | operator bool() const { 63 | return IsValid(); 64 | } 65 | 66 | void Close(); 67 | 68 | ~StructuredDirectory() { 69 | Close(); 70 | } 71 | 72 | CompoundFileMode GetMode() const { 73 | return m_FileMode; 74 | } 75 | 76 | std::wstring GetName() const; 77 | 78 | protected: 79 | IStorage* GetStorage() const { 80 | return m_spStorage.p; 81 | } 82 | 83 | StructuredDirectory(IStorage* pStg, CompoundFileMode mode = CompoundFileMode::ReadWrite) : m_spStorage(pStg), m_FileMode(mode) { 84 | } 85 | 86 | bool CheckNameLength(const std::wstring& name) const; 87 | 88 | private: 89 | CComPtr m_spStorage; 90 | CompoundFileMode m_FileMode; 91 | }; 92 | 93 | class CompoundFile : public StructuredDirectory { 94 | public: 95 | // factory methods 96 | 97 | static CompoundFile Create(const std::wstring& path, PSECURITY_DESCRIPTOR securityDescriptor = nullptr); 98 | static CompoundFile Open(const std::wstring& path, CompoundFileMode mode = CompoundFileMode::Read); 99 | 100 | // ctors 101 | 102 | CompoundFile(const CompoundFile&) = delete; 103 | CompoundFile& operator=(const CompoundFile&) = delete; 104 | 105 | CompoundFile(CompoundFile&&) = default; 106 | CompoundFile& operator=(CompoundFile&&) = default; 107 | 108 | private: 109 | CompoundFile(IStorage* pStg, CompoundFileMode mode = CompoundFileMode::ReadWrite) : StructuredDirectory(pStg, mode) {} 110 | }; 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /BasicDemo/MainFrm.cpp: -------------------------------------------------------------------------------- 1 | // MainFrm.cpp : implmentation of the CMainFrame class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "pch.h" 6 | #include "resource.h" 7 | #include "AboutDlg.h" 8 | #include "ProcessTreeListView.h" 9 | #include "MainFrm.h" 10 | 11 | const int WINDOW_MENU_POSITION = 5; 12 | 13 | BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { 14 | if (CFrameWindowImpl::PreTranslateMessage(pMsg)) 15 | return TRUE; 16 | 17 | return m_view.PreTranslateMessage(pMsg); 18 | } 19 | 20 | BOOL CMainFrame::OnIdle() { 21 | return FALSE; 22 | } 23 | 24 | LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { 25 | CreateSimpleStatusBar(); 26 | 27 | m_hWndClient = m_view.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE); 28 | UISetCheck(ID_VIEW_STATUS_BAR, 1); 29 | 30 | // register object for message filtering and idle updates 31 | CMessageLoop* pLoop = _Module.GetMessageLoop(); 32 | ATLASSERT(pLoop != nullptr); 33 | pLoop->AddMessageFilter(this); 34 | pLoop->AddIdleHandler(this); 35 | 36 | CMenuHandle menuMain = GetMenu(); 37 | m_view.SetWindowMenu(menuMain.GetSubMenu(WINDOW_MENU_POSITION)); 38 | 39 | return 0; 40 | } 41 | 42 | LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { 43 | // unregister message filtering and idle updates 44 | CMessageLoop* pLoop = _Module.GetMessageLoop(); 45 | ATLASSERT(pLoop != nullptr); 46 | pLoop->RemoveMessageFilter(this); 47 | pLoop->RemoveIdleHandler(this); 48 | 49 | bHandled = FALSE; 50 | return 1; 51 | } 52 | 53 | LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 54 | PostMessage(WM_CLOSE); 55 | return 0; 56 | } 57 | 58 | LRESULT CMainFrame::OnTreeListDemo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 59 | auto pView = new CProcessTreeListView; 60 | pView->Create(m_view, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0); 61 | m_view.AddPage(pView->m_hWnd, _T("Process Tree List")); 62 | 63 | // TODO: add code to initialize document 64 | 65 | return 0; 66 | } 67 | 68 | LRESULT CMainFrame::OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 69 | BOOL bVisible = !::IsWindowVisible(m_hWndStatusBar); 70 | ::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE); 71 | UISetCheck(ID_VIEW_STATUS_BAR, bVisible); 72 | UpdateLayout(); 73 | return 0; 74 | } 75 | 76 | LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 77 | CAboutDlg dlg; 78 | dlg.DoModal(); 79 | return 0; 80 | } 81 | 82 | LRESULT CMainFrame::OnWindowClose(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 83 | int nActivePage = m_view.GetActivePage(); 84 | if (nActivePage != -1) 85 | m_view.RemovePage(nActivePage); 86 | else 87 | ::MessageBeep((UINT)-1); 88 | 89 | return 0; 90 | } 91 | 92 | LRESULT CMainFrame::OnWindowCloseAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 93 | m_view.RemoveAllPages(); 94 | 95 | return 0; 96 | } 97 | 98 | LRESULT CMainFrame::OnWindowActivate(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { 99 | int nPage = wID - ID_WINDOW_TABFIRST; 100 | m_view.SetActivePage(nPage); 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /WTLHelper/IATHook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This file contains code from 4 | // https://github.com/stevemk14ebr/PolyHook_2_0/blob/master/sources/IatHook.cpp 5 | // which is licensed under the MIT License. 6 | // See PolyHook_2_0-LICENSE for more information. 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | template 13 | constexpr T RVA2VA(T1 base, T2 rva) { 14 | return reinterpret_cast(reinterpret_cast(base) + rva); 15 | } 16 | 17 | template 18 | constexpr T DataDirectoryFromModuleBase(void* moduleBase, size_t entryID) { 19 | auto dosHdr = reinterpret_cast(moduleBase); 20 | auto ntHdr = RVA2VA(moduleBase, dosHdr->e_lfanew); 21 | auto dataDir = ntHdr->OptionalHeader.DataDirectory; 22 | return RVA2VA(moduleBase, dataDir[entryID].VirtualAddress); 23 | } 24 | 25 | PIMAGE_THUNK_DATA FindAddressByName(void* moduleBase, PIMAGE_THUNK_DATA impName, PIMAGE_THUNK_DATA impAddr, const char* funcName) { 26 | for (; impName->u1.Ordinal; ++impName, ++impAddr) { 27 | if (IMAGE_SNAP_BY_ORDINAL(impName->u1.Ordinal)) 28 | continue; 29 | 30 | auto import = RVA2VA(moduleBase, impName->u1.AddressOfData); 31 | if (strcmp(import->Name, funcName) != 0) 32 | continue; 33 | return impAddr; 34 | } 35 | return nullptr; 36 | } 37 | 38 | PIMAGE_THUNK_DATA FindAddressByOrdinal(void* moduleBase, PIMAGE_THUNK_DATA impName, PIMAGE_THUNK_DATA impAddr, uint16_t ordinal) { 39 | for (; impName->u1.Ordinal; ++impName, ++impAddr) { 40 | if (IMAGE_SNAP_BY_ORDINAL(impName->u1.Ordinal) && IMAGE_ORDINAL(impName->u1.Ordinal) == ordinal) 41 | return impAddr; 42 | } 43 | return nullptr; 44 | } 45 | 46 | PIMAGE_THUNK_DATA FindIatThunkInModule(void* moduleBase, const char* dllName, const char* funcName) { 47 | auto imports = DataDirectoryFromModuleBase(moduleBase, IMAGE_DIRECTORY_ENTRY_IMPORT); 48 | for (; imports->Name; ++imports) { 49 | if (_stricmp(RVA2VA(moduleBase, imports->Name), dllName) != 0) 50 | continue; 51 | 52 | auto origThunk = RVA2VA(moduleBase, imports->OriginalFirstThunk); 53 | auto thunk = RVA2VA(moduleBase, imports->FirstThunk); 54 | return FindAddressByName(moduleBase, origThunk, thunk, funcName); 55 | } 56 | return nullptr; 57 | } 58 | 59 | PIMAGE_THUNK_DATA FindDelayLoadThunkInModule(void* moduleBase, const char* dllName, const char* funcName) { 60 | auto imports = DataDirectoryFromModuleBase(moduleBase, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); 61 | for (; imports->DllNameRVA; ++imports) { 62 | if (_stricmp(RVA2VA(moduleBase, imports->DllNameRVA), dllName) != 0) 63 | continue; 64 | 65 | auto impName = RVA2VA(moduleBase, imports->ImportNameTableRVA); 66 | auto impAddr = RVA2VA(moduleBase, imports->ImportAddressTableRVA); 67 | return FindAddressByName(moduleBase, impName, impAddr, funcName); 68 | } 69 | return nullptr; 70 | } 71 | 72 | PIMAGE_THUNK_DATA FindDelayLoadThunkInModule(void* moduleBase, const char* dllName, uint16_t ordinal) { 73 | auto imports = DataDirectoryFromModuleBase(moduleBase, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); 74 | for (; imports->DllNameRVA; ++imports) { 75 | if (_stricmp(RVA2VA(moduleBase, imports->DllNameRVA), dllName) != 0) 76 | continue; 77 | 78 | auto impName = RVA2VA(moduleBase, imports->ImportNameTableRVA); 79 | auto impAddr = RVA2VA(moduleBase, imports->ImportAddressTableRVA); 80 | return FindAddressByOrdinal(moduleBase, impName, impAddr, ordinal); 81 | } 82 | return nullptr; 83 | } 84 | -------------------------------------------------------------------------------- /WTLHelper/QuickFindEdit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | #include "Theme.h" 5 | 6 | const UINT EN_DELAYCHANGE = EN_CHANGE + 1; 7 | 8 | class CQuickFindEdit : public CWindowImpl { 9 | public: 10 | BEGIN_MSG_MAP(CQuickFindEdit) 11 | MESSAGE_HANDLER(WM_TIMER, OnTimer) 12 | MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) 13 | MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode) 14 | MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) 15 | MESSAGE_HANDLER(WM_SETFOCUS, OnKillFocus) 16 | MESSAGE_HANDLER(WM_CHAR, OnChar) 17 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 18 | MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnDialogColor) 19 | MESSAGE_HANDLER(WM_CTLCOLOREDIT, OnDialogColor) 20 | MESSAGE_HANDLER(WM_PAINT, OnPaint) 21 | END_MSG_MAP() 22 | 23 | ~CQuickFindEdit() { 24 | if (m_hIcon) 25 | ::DestroyIcon(m_hIcon); 26 | } 27 | 28 | bool SetHotKey(UINT modifiers, UINT virtKey) { 29 | if (m_HotKeyId) 30 | ::UnregisterHotKey(m_hWnd, 1); 31 | auto ok = ::RegisterHotKey(m_hWnd, 1, modifiers, virtKey); 32 | if (ok) { 33 | m_HotKeyId = 1; 34 | } 35 | return ok; 36 | } 37 | 38 | void SetWatermark(PCWSTR watermark) { 39 | m_Watermark = watermark; 40 | } 41 | 42 | void SetTextChangeDelay(UINT ms) { 43 | m_Delay = ms; 44 | } 45 | 46 | void SetWatermarkIcon(HICON hIcon) { 47 | m_hIcon = hIcon; 48 | } 49 | 50 | LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 51 | return wParam == VK_TAB ? 0 : DLGC_WANTALLKEYS; 52 | } 53 | 54 | LRESULT OnHotKey(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 55 | if (wParam == 1) 56 | SetFocus(); 57 | return 0; 58 | } 59 | 60 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { 61 | CDCHandle dc((HDC)wParam); 62 | CRect rc; 63 | GetClientRect(&rc); 64 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 65 | return 1; 66 | } 67 | 68 | LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 69 | if (wParam == 1) { 70 | KillTimer(1); 71 | GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(GWL_ID), EN_DELAYCHANGE), reinterpret_cast(m_hWnd)); 72 | } 73 | return 0; 74 | } 75 | 76 | LRESULT OnDialogColor(UINT, WPARAM, LPARAM, BOOL&) { 77 | return (LRESULT)::GetSysColorBrush(COLOR_WINDOW); 78 | } 79 | 80 | LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 81 | if (GetWindowTextLength() == 0 && !m_Watermark.IsEmpty() && ::GetFocus() != m_hWnd) { 82 | CPaintDC dc(m_hWnd); 83 | dc.SetBkMode(TRANSPARENT); 84 | dc.SetTextColor(m_WatermarkColor); 85 | dc.SelectFont(GetFont()); 86 | CRect rc; 87 | GetClientRect(&rc); 88 | rc.left += 4; 89 | if (m_hIcon) { 90 | dc.DrawIconEx(rc.left, rc.Height() / 2 - 8, m_hIcon, 16, 16); 91 | rc.left += 20; 92 | } 93 | dc.DrawText(m_Watermark, -1, &rc, DT_VCENTER | DT_SINGLELINE); 94 | } 95 | else { 96 | DefWindowProc(); 97 | } 98 | return 0; 99 | } 100 | 101 | LRESULT OnKillFocus(UINT msg, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 102 | if (!m_Watermark.IsEmpty()) 103 | Invalidate(); 104 | if (msg == WM_SETFOCUS) 105 | SetSelAll(); 106 | bHandled = FALSE; 107 | return 0; 108 | } 109 | 110 | LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 111 | if (wParam == VK_ESCAPE) 112 | SetWindowText(L""); 113 | else 114 | bHandled = FALSE; 115 | SetTimer(1, m_Delay); 116 | return 0; 117 | } 118 | 119 | private: 120 | CString m_Watermark; 121 | COLORREF m_WatermarkColor{ RGB(128, 128, 128) }; 122 | UINT m_Delay{ 250 }; 123 | HICON m_hIcon{ nullptr }; 124 | UINT m_HotKeyId{ 0 }; 125 | }; 126 | -------------------------------------------------------------------------------- /WTLHelper/CompoundFile.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CompoundFile.h" 3 | 4 | using namespace std; 5 | using namespace StructuredStorage; 6 | 7 | CompoundFile CompoundFile::Create(const std::wstring& path, PSECURITY_DESCRIPTOR securityDescriptor) { 8 | CComPtr spStg; 9 | auto hr = ::StgCreateStorageEx(path.c_str(), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 10 | STGFMT_STORAGE, 0, nullptr, securityDescriptor, __uuidof(IStorage), reinterpret_cast(&spStg)); 11 | 12 | return CompoundFile(spStg); 13 | } 14 | 15 | CompoundFile CompoundFile::Open(const std::wstring& path, CompoundFileMode mode) { 16 | CComPtr spStg; 17 | auto hr = ::StgOpenStorageEx(path.c_str(), (mode == CompoundFileMode::Read ? STGM_READ : STGM_READWRITE) | STGM_SHARE_EXCLUSIVE, 18 | STGFMT_STORAGE, 0, nullptr, nullptr, __uuidof(IStorage), reinterpret_cast(&spStg)); 19 | 20 | return CompoundFile(spStg, mode); 21 | } 22 | 23 | StructuredDirectory StructuredDirectory::CreateStructuredDirectory(const std::wstring& name) { 24 | CheckNameLength(name); 25 | 26 | CComPtr spStg; 27 | auto hr = GetStorage()->CreateStorage(name.c_str(), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &spStg); 28 | 29 | return StructuredDirectory(spStg); 30 | } 31 | 32 | StructuredFile StructuredDirectory::CreateStructuredFile(const std::wstring& name) { 33 | CheckNameLength(name); 34 | 35 | CComPtr spStm; 36 | auto hr = GetStorage()->CreateStream(name.c_str(), STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &spStm); 37 | 38 | return StructuredFile(spStm); 39 | } 40 | 41 | StructuredFile StructuredDirectory::OpenStructuredFile(const std::wstring& name) const { 42 | CheckNameLength(name); 43 | 44 | CComPtr spStm; 45 | auto hr = GetStorage()->OpenStream(name.c_str(), nullptr, 46 | (GetMode() == CompoundFileMode::Read ? STGM_READ : STGM_READWRITE) | STGM_SHARE_EXCLUSIVE, 0, &spStm); 47 | 48 | return StructuredFile(spStm); 49 | } 50 | 51 | StructuredDirectory StructuredDirectory::OpenStructuredDirectory(const std::wstring& name) const { 52 | CheckNameLength(name); 53 | 54 | CComPtr spStg; 55 | auto hr = GetStorage()->OpenStorage(name.c_str(), nullptr, 56 | (GetMode() == CompoundFileMode::Read ? STGM_READ : STGM_READWRITE) | STGM_SHARE_EXCLUSIVE, 0, 0, &spStg); 57 | 58 | return StructuredDirectory(spStg, GetMode()); 59 | } 60 | 61 | void StructuredDirectory::Close() { 62 | m_spStorage = nullptr; 63 | } 64 | 65 | bool StructuredDirectory::CheckNameLength(const std::wstring& name) const { 66 | return name.size() < 32; 67 | } 68 | 69 | HRESULT StructuredFile::Write(const void* buffer, uint32_t count) { 70 | return m_spStream->Write(buffer, count, nullptr); 71 | } 72 | 73 | HRESULT StructuredFile::Read(void* buffer, uint32_t count) const { 74 | return m_spStream->Read(buffer, count, nullptr); 75 | } 76 | 77 | HRESULT StructuredFile::Seek(uint32_t offset, SeekMode mode, uint32_t* newOffset) { 78 | LARGE_INTEGER li; 79 | li.QuadPart = offset; 80 | ULARGE_INTEGER newOffsetLocal; 81 | auto hr = m_spStream->Seek(li, static_cast(mode), &newOffsetLocal); 82 | if (SUCCEEDED(hr) && newOffset) 83 | *newOffset = newOffsetLocal.LowPart; 84 | return hr; 85 | } 86 | 87 | uint32_t StructuredFile::GetSize() const { 88 | STATSTG stat = { 0 }; 89 | m_spStream->Stat(&stat, STATFLAG_NONAME); 90 | return stat.cbSize.LowPart; 91 | } 92 | 93 | void StructuredFile::Close() { 94 | m_spStream = nullptr; 95 | } 96 | 97 | std::wstring StructuredFile::GetName() const { 98 | STATSTG stat = { 0 }; 99 | m_spStream->Stat(&stat, STATFLAG_DEFAULT); 100 | std::wstring result(stat.pwcsName); 101 | ::CoTaskMemFree(stat.pwcsName); 102 | return result; 103 | } 104 | 105 | std::wstring StructuredDirectory::GetName() const { 106 | STATSTG stat = { 0 }; 107 | m_spStorage->Stat(&stat, STATFLAG_DEFAULT); 108 | std::wstring result(stat.pwcsName); 109 | ::CoTaskMemFree(stat.pwcsName); 110 | return result; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /WTLHelper/OwnerDrawnMenu.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "OwnerDrawnMenu.h" 3 | 4 | void COwnerDrawnMenuBase::SetTextColor(COLORREF color) { 5 | m_TextColor = color; 6 | } 7 | 8 | void COwnerDrawnMenuBase::SetBackColor(COLORREF color) { 9 | m_BackColor = color; 10 | } 11 | 12 | void COwnerDrawnMenuBase::SetSelectionTextColor(COLORREF color) { 13 | m_SelectionTextColor = color; 14 | } 15 | 16 | void COwnerDrawnMenuBase::SetSelectionBackColor(COLORREF color) { 17 | m_SelectionBackColor = color; 18 | } 19 | 20 | void COwnerDrawnMenuBase::SetSeparatorColor(COLORREF color) { 21 | m_SeparatorColor = color; 22 | } 23 | 24 | void COwnerDrawnMenuBase::UpdateMenuBase(CMenuHandle menu, bool subMenus) { 25 | MENUINFO mi = { sizeof(mi) }; 26 | mi.fMask = MIM_BACKGROUND | (subMenus ? MIM_APPLYTOSUBMENUS : 0); 27 | CBrush brush; 28 | brush.CreateSolidBrush(m_BackColor); 29 | mi.hbrBack = brush.Detach(); 30 | ATLVERIFY(menu.SetMenuInfo(&mi)); 31 | } 32 | 33 | void COwnerDrawnMenuBase::AddCommand(UINT id, HICON hIcon) { 34 | ItemData data; 35 | data.Image = m_Images.AddIcon(hIcon); 36 | auto it = m_Items.find(id); 37 | if (it != m_Items.end()) 38 | it->second.Image = data.Image; 39 | else 40 | m_Items.insert({ id, data }); 41 | } 42 | 43 | void COwnerDrawnMenuBase::AddCommand(UINT id, UINT iconId) { 44 | auto hIcon = AtlLoadIconImage(iconId, 64, 16, 16); 45 | ATLASSERT(hIcon); 46 | AddCommand(id, hIcon); 47 | } 48 | 49 | bool COwnerDrawnMenuBase::AddMenu(UINT id) { 50 | CMenu menu; 51 | return menu.LoadMenu(id) && AddMenu(menu.m_hMenu); 52 | } 53 | 54 | bool COwnerDrawnMenuBase::AddMenu(CMenuHandle menu) { 55 | ATLASSERT(::IsMenu(menu.m_hMenu)); 56 | UpdateMenuBase(menu, true); 57 | auto count = menu.GetMenuItemCount(); 58 | MENUITEMINFO mii = { sizeof(mii) }; 59 | WCHAR text[64]; 60 | for (decltype(count) i = 0; i < count; i++) { 61 | mii.fMask = MIIM_SUBMENU | MIIM_ID; 62 | if (menu.GetMenuItemInfo(i, TRUE, &mii) && mii.hSubMenu) { 63 | AddSubMenu(mii.hSubMenu); 64 | mii.fMask = MIIM_TYPE | MIIM_DATA; 65 | mii.fType |= MFT_OWNERDRAW; 66 | mii.dwItemData = TopLevelMenu; // top level menu 67 | ATLVERIFY(menu.SetMenuItemInfo(i, TRUE, &mii)); 68 | if (menu.GetMenuString(i, text, _countof(text), MF_BYPOSITION)) { 69 | ItemData data; 70 | data.Text = text; 71 | data.Image = -1; 72 | m_Items.insert({ mii.wID, data }); 73 | } 74 | } 75 | } 76 | return true; 77 | } 78 | 79 | void COwnerDrawnMenuBase::AddSubMenu(CMenuHandle menu) { 80 | UpdateMenuBase(menu); 81 | auto count = menu.GetMenuItemCount(); 82 | MENUITEMINFO mii = { sizeof(mii) }; 83 | WCHAR text[64]; 84 | for (decltype(count) i = 0; i < count; i++) { 85 | mii.fMask = MIIM_TYPE | MIIM_DATA; 86 | mii.fType = 0; 87 | if (menu.GetMenuItemInfoW(i, TRUE, &mii) && mii.fType == MFT_SEPARATOR) 88 | mii.dwItemData = Separator; 89 | else 90 | mii.dwItemData = 0; 91 | if (mii.fType & MFT_OWNERDRAW) 92 | continue; 93 | 94 | mii.fType |= MFT_OWNERDRAW; 95 | ATLVERIFY(menu.SetMenuItemInfo(i, TRUE, &mii)); 96 | if (mii.dwItemData == Separator) 97 | continue; 98 | 99 | mii.fMask = MIIM_SUBMENU | MIIM_ID; 100 | if (menu.GetMenuItemInfo(i, TRUE, &mii) && mii.hSubMenu) { 101 | AddSubMenu(mii.hSubMenu); 102 | } 103 | else { 104 | if (menu.GetMenuString(i, text, _countof(text), MF_BYPOSITION)) { 105 | ATLASSERT(text[0]); 106 | ATLASSERT(mii.wID); 107 | auto it = m_Items.find(mii.wID); 108 | if (it != m_Items.end()) 109 | it->second.Text = text; 110 | else { 111 | ItemData data; 112 | data.Text = text; 113 | data.Image = -1; 114 | m_Items.insert({ mii.wID, data }); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | void COwnerDrawnMenuBase::SetCheckIcon(HICON hIcon, HICON hRadioIcon) { 122 | m_CheckIcon = m_Images.AddIcon(hIcon); 123 | if (hRadioIcon) 124 | m_RadioIcon = m_Images.AddIcon(hRadioIcon); 125 | } 126 | 127 | void COwnerDrawnMenuBase::SetCheckIcon(UINT iconId, UINT radioId) { 128 | SetCheckIcon(AtlLoadIconImage(iconId, 0, 16, 16), radioId ? AtlLoadIconImage(radioId, 0, 16, 16) : nullptr); 129 | } 130 | -------------------------------------------------------------------------------- /WTLHelper/CompoundFileReaderWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "CompoundFile.h" 7 | 8 | namespace StructuredStorage { 9 | class CompoundFileWriter { 10 | public: 11 | explicit CompoundFileWriter(StructuredFile& file); 12 | CompoundFileWriter(StructuredDirectory& dir, const std::wstring& name); 13 | 14 | template 15 | void Write(const T& value) { 16 | static_assert(std::is_trivially_copyable(), "T must be POD"); 17 | 18 | m_File.Write(&value, sizeof(value)); 19 | } 20 | 21 | void Write(const std::wstring& value); 22 | void Write(PCWSTR value) { 23 | return Write(std::wstring(value)); 24 | } 25 | void Write(CString const& value) { 26 | return Write(std::wstring((PCWSTR)value)); 27 | } 28 | void Write(const std::string& value); 29 | 30 | template 31 | void Write(const std::vector& vec) { 32 | auto count = static_cast(vec.size()); 33 | Write(count); 34 | if (std::is_pod::value) { 35 | m_File.Write(vec.data(), count * sizeof(T)); 36 | } 37 | else { 38 | for (const auto& item : vec) 39 | Write(item); 40 | } 41 | } 42 | 43 | template 44 | void Write(const std::pair& pair) { 45 | Write(pair.first); 46 | Write(pair.second); 47 | } 48 | 49 | template 50 | void Write(const std::map& map) { 51 | Write(static_cast(map.size())); 52 | for (const auto& pair : map) 53 | Write(pair); 54 | } 55 | 56 | template 57 | void Write(const std::unordered_map& map) { 58 | Write(static_cast(map.size())); 59 | for (const auto& pair : map) 60 | Write(pair); 61 | } 62 | 63 | private: 64 | StructuredFile m_File; 65 | }; 66 | 67 | class CompoundFileReader { 68 | public: 69 | CompoundFileReader(const StructuredFile& file); 70 | CompoundFileReader(const StructuredDirectory& dir, const std::wstring& name); 71 | 72 | 73 | template 74 | void Read(T& value) const { 75 | static_assert(std::is_trivially_copyable(), "T must be POD"); 76 | 77 | m_File.Read(&value, sizeof(value)); 78 | } 79 | 80 | void Read(std::wstring& value) const; 81 | void Read(std::string& value) const; 82 | 83 | template 84 | void Read(std::vector& vec) const { 85 | uint32_t count; 86 | Read(count); 87 | if (count == 0) { 88 | vec.clear(); 89 | return; 90 | } 91 | 92 | vec.resize(count); 93 | if (std::is_pod::value) { 94 | m_File.Read(vec.data(), count * sizeof(T)); 95 | } 96 | else { 97 | for (uint32_t i = 0; i < count; ++i) { 98 | T value; 99 | Read(value); 100 | vec[i] = value; 101 | } 102 | } 103 | } 104 | 105 | template 106 | void Read(std::pair& pair) const { 107 | Read(pair.first); 108 | Read(pair.second); 109 | } 110 | 111 | template 112 | void Read(std::map& map) const { 113 | uint32_t count; 114 | Read(count); 115 | for (uint32_t i = 0; i < count; i++) { 116 | std::pair pair; 117 | Read(pair); 118 | map.insert(pair); 119 | } 120 | } 121 | 122 | template 123 | void Read(std::unordered_map& map) const { 124 | uint32_t count; 125 | Read(count); 126 | for (uint32_t i = 0; i < count; i++) { 127 | std::pair pair; 128 | Read(pair); 129 | map.insert(pair); 130 | } 131 | } 132 | 133 | private: 134 | const StructuredFile m_File; 135 | }; 136 | 137 | template 138 | bool CreateFileAndWrite(StructuredDirectory& dir, const std::wstring& name, T&& value) { 139 | auto file = dir.CreateStructuredFile(name); 140 | if (!file) 141 | return false; 142 | CompoundFileWriter writer(file); 143 | writer.Write(std::forward(value)); 144 | return true; 145 | } 146 | 147 | template 148 | bool OpenFileAndRead(const StructuredDirectory& dir, const std::wstring& name, T& value) { 149 | auto file = dir.OpenStructuredFile(name); 150 | if (!file) 151 | return false; 152 | CompoundFileReader reader(file); 153 | reader.Read(value); 154 | return true; 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /WTLHelper/IniFile.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "IniFile.h" 3 | #include 4 | 5 | IniFile::IniFile(PCWSTR path) : _path(path) { 6 | } 7 | 8 | bool IniFile::IsValid() const { 9 | return ::GetFileAttributes(_path) != INVALID_FILE_ATTRIBUTES; 10 | } 11 | 12 | CString IniFile::ReadString(PCWSTR section, PCWSTR name, PCWSTR defaultValue) { 13 | CString result; 14 | auto count = ::GetPrivateProfileString(section, name, defaultValue, result.GetBufferSetLength(128), 128, _path); 15 | return result; 16 | } 17 | 18 | bool IniFile::ReadBool(PCWSTR section, PCWSTR name, bool defaultValue) { 19 | auto value = ReadInt(section, name, -1); 20 | if (value == -1) 21 | return defaultValue; 22 | return value ? true : false; 23 | } 24 | 25 | int IniFile::ReadInt(PCWSTR section, PCWSTR name, int defaultValue) { 26 | return ::GetPrivateProfileInt(section, name, defaultValue, _path); 27 | } 28 | 29 | COLORREF IniFile::ReadColor(PCWSTR section, PCWSTR name, COLORREF defaultValue) { 30 | auto text = ReadString(section, name); 31 | if (text.IsEmpty()) 32 | return defaultValue; 33 | 34 | if (text.Left(2) == L"0x") 35 | return ParseHexColor(text.Mid(2)); 36 | 37 | if (text.Find(L',')) 38 | return ParseDecColor(text); 39 | 40 | return ParseHexColor(text); 41 | } 42 | 43 | COLORREF IniFile::ParseHexColor(const CString& hex) { 44 | std::wstringstream ss; 45 | DWORD color; 46 | ss << std::hex << hex; 47 | ss >> color; 48 | return color; 49 | } 50 | 51 | COLORREF IniFile::ParseDecColor(const CString& text) { 52 | int start = 0; 53 | COLORREF color = 0; 54 | auto str = text.Tokenize(L",", start); 55 | if (str.IsEmpty()) 56 | return CLR_INVALID; 57 | color |= _wtoi(str); 58 | str = text.Tokenize(L",", start); 59 | if (str.IsEmpty()) 60 | return CLR_INVALID; 61 | color |= _wtoi(str) << 8; 62 | str = text.Tokenize(L",", start); 63 | if (str.IsEmpty()) 64 | return CLR_INVALID; 65 | color |= _wtoi(str) << 16; 66 | 67 | return color; 68 | } 69 | 70 | std::vector IniFile::ReadSection(PCWSTR section) { 71 | WCHAR buffer[1 << 10]; 72 | std::vector names; 73 | if (0 == ::GetPrivateProfileSection(section, buffer, _countof(buffer), _path)) 74 | return names; 75 | 76 | names.reserve(8); 77 | for (auto p = buffer; *p; ) { 78 | names.push_back(p); 79 | p += names.back().GetLength() + 1; 80 | } 81 | return names; 82 | } 83 | 84 | bool IniFile::WriteString(PCWSTR section, PCWSTR name, PCWSTR value) { 85 | return ::WritePrivateProfileString(section, name, value, _path); 86 | } 87 | 88 | bool IniFile::WriteInt(PCWSTR section, PCWSTR name, int value, bool hex) { 89 | CString text; 90 | text.Format(hex ? L"0x%X" : L"%d", value); 91 | return WriteString(section, name, text); 92 | } 93 | 94 | bool IniFile::WriteBool(PCWSTR section, PCWSTR name, bool value) { 95 | return WriteInt(section, name, value ? 1 : 0); 96 | } 97 | 98 | bool IniFile::WriteColor(PCWSTR section, PCWSTR name, COLORREF color) { 99 | CString text; 100 | text.Format(L"%d,%d,%d", GetRValue(color), GetGValue(color), GetBValue(color)); 101 | return WriteString(section, name, text); 102 | } 103 | 104 | bool IniFile::WriteFont(PCWSTR section, PCWSTR name, const LOGFONT& font) { 105 | return ::WritePrivateProfileStruct(section, name, (PVOID)&font, sizeof(font), _path); 106 | } 107 | 108 | bool IniFile::WriteBinary(PCWSTR section, PCWSTR name, void* data, unsigned size) { 109 | WriteInt(section, name + CString(L"_size"), size); 110 | return ::WritePrivateProfileStruct(section, name, data, size, _path); 111 | } 112 | 113 | bool IniFile::ReadFont(PCWSTR section, PCWSTR name, LOGFONT& font) { 114 | return ::GetPrivateProfileStruct(section, name, &font, sizeof(font), _path); 115 | } 116 | 117 | std::unique_ptr IniFile::ReadBinary(PCWSTR section, PCWSTR name, unsigned& size) { 118 | size = (unsigned)ReadInt(section, name + CString(L"_size")); 119 | if (size == 0) 120 | return nullptr; 121 | auto buffer = std::make_unique(size); 122 | if (buffer == nullptr) 123 | return nullptr; 124 | ::GetPrivateProfileStruct(section, name, buffer.get(), size, _path); 125 | return buffer; 126 | } 127 | 128 | -------------------------------------------------------------------------------- /WTLHelper/ColumnManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | enum class ColumnFlags { 7 | None = 0, 8 | Visible = 1, 9 | Fixed = 2, 10 | Const = 4, // currently unused 11 | Mandatory = 8, 12 | Numeric = 0x10, 13 | Modified = 0x80 14 | }; 15 | DEFINE_ENUM_FLAG_OPERATORS(ColumnFlags); 16 | 17 | class ColumnManager { 18 | public: 19 | struct ColumnInfo { 20 | int DefaultWidth; 21 | int Format; 22 | CString Name; 23 | ColumnFlags Flags; 24 | CString Category; 25 | int Tag; 26 | 27 | bool IsVisible() const; 28 | bool IsMandatory() const; 29 | void SetVisible(bool); 30 | }; 31 | 32 | explicit ColumnManager(HWND hListView = nullptr) : m_ListView(hListView) {} 33 | HWND Attach(HWND hListView); 34 | ~ColumnManager(); 35 | 36 | ColumnManager(const ColumnManager&) = default; 37 | ColumnManager& operator=(const ColumnManager&) = delete; 38 | 39 | HWND GetListView() const { 40 | return m_ListView; 41 | } 42 | 43 | bool CopyTo(ColumnManager& other) const; 44 | void AddFromControl(HWND hList = nullptr); 45 | void SetVisible(int column, bool visible); 46 | bool IsVisible(int column) const; 47 | bool IsModified(int column) const; 48 | void SetModified(int column, bool modified); 49 | bool IsConst(int column) const; 50 | bool DeleteColumn(int column); 51 | 52 | template 53 | int AddColumn(PCWSTR name, int format, int width, T tag = T(), ColumnFlags flags = ColumnFlags::Visible); 54 | 55 | template 56 | T GetColumnTag(int index) const { 57 | return static_cast(m_Columns[index].Tag); 58 | } 59 | template 60 | int GetColumnByTag(T tag) { 61 | for (int i = 0; i < GetCount(); i++) 62 | if (m_Columns[i].Tag == static_cast(tag)) 63 | return i; 64 | return -1; 65 | } 66 | 67 | void Clear(); 68 | const ColumnInfo& GetColumn(int index) const; 69 | const std::vector& GetColumnsByCategory(PCWSTR category) const; 70 | const std::vector& GetCategories() const; 71 | 72 | void UpdateColumns(); 73 | int GetRealColumn(int index) const; 74 | 75 | int GetCount() const { 76 | return static_cast(m_Columns.size()); 77 | } 78 | 79 | protected: 80 | void SetColumn(int i, const ColumnInfo& info); 81 | 82 | private: 83 | CListViewCtrl m_ListView; 84 | std::vector m_Columns; 85 | std::map> m_ColumnsByCategory; 86 | std::vector m_Categories; 87 | }; 88 | 89 | inline bool ColumnManager::ColumnInfo::IsVisible() const { 90 | return (Flags & ColumnFlags::Visible) == ColumnFlags::Visible; 91 | } 92 | 93 | inline bool ColumnManager::ColumnInfo::IsMandatory() const { 94 | return (Flags & ColumnFlags::Mandatory) == ColumnFlags::Mandatory; 95 | } 96 | 97 | inline void ColumnManager::ColumnInfo::SetVisible(bool visible) { 98 | bool old = (Flags & ColumnFlags::Visible) == ColumnFlags::Visible; 99 | if (old == visible) 100 | return; 101 | 102 | if (visible) 103 | Flags |= ColumnFlags::Visible; 104 | else 105 | Flags &= ~ColumnFlags::Visible; 106 | Flags |= ColumnFlags::Modified; 107 | } 108 | 109 | template 110 | int ColumnManager::AddColumn(PCWSTR name, int format, int width, T tag, ColumnFlags flags) { 111 | auto category = ::wcschr(name, L'\\'); 112 | CString categoryName; 113 | if (category) { 114 | categoryName = CString(name, static_cast(category - name)); 115 | name = category + 1; 116 | } 117 | else { 118 | categoryName = L"General"; 119 | } 120 | ColumnInfo info; 121 | info.Format = format; 122 | info.DefaultWidth = width; 123 | info.Flags = flags; 124 | info.Name = name; 125 | info.Category = categoryName; 126 | info.Tag = static_cast(tag); 127 | 128 | if (m_ListView && ((flags & ColumnFlags::Visible) == ColumnFlags::Visible)) { 129 | auto header = m_ListView.GetHeader(); 130 | int i = m_ListView.InsertColumn(header.GetItemCount(), name, format, width); 131 | HDITEM hdi; 132 | hdi.mask = HDI_LPARAM; 133 | hdi.lParam = m_Columns.size(); 134 | header.SetItem(i, &hdi); 135 | } 136 | 137 | m_Columns.push_back(info); 138 | if (!categoryName.IsEmpty()) { 139 | if (std::find(m_Categories.begin(), m_Categories.end(), categoryName) == m_Categories.end()) 140 | m_Categories.push_back(categoryName); 141 | m_ColumnsByCategory[categoryName].push_back(static_cast(m_Columns.size() - 1)); 142 | } 143 | 144 | return static_cast(m_Columns.size()); 145 | } 146 | -------------------------------------------------------------------------------- /WTLHelper/CustomStatusBar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ThemeHelper.h" 4 | #include "SizeGrip.h" 5 | 6 | class CCustomStatusBar : 7 | public CWindowImpl, 8 | public COwnerDraw { 9 | public: 10 | BEGIN_MSG_MAP(CCustomStatusBar) 11 | MESSAGE_HANDLER(SB_SETTEXT, OnSetText) 12 | MESSAGE_HANDLER(SB_GETTEXT, OnGetText) 13 | MESSAGE_HANDLER(SB_GETTEXTLENGTH, OnGetTextLength) 14 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 15 | MESSAGE_HANDLER(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), OnUpdateTheme) 16 | //MESSAGE_HANDLER(WM_PAINT, OnPaint) 17 | CHAIN_MSG_MAP_ALT(COwnerDraw, 0) 18 | END_MSG_MAP() 19 | 20 | LRESULT OnUpdateTheme(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/) { 21 | auto theme = reinterpret_cast(lParam); 22 | SetBkColor(theme->StatusBar.BackColor); 23 | 24 | return 0; 25 | } 26 | 27 | void Init(HWND hWnd, LPCREATESTRUCT cs) { 28 | SubclassWindow(hWnd); 29 | m_Text.push_back(cs->lpszName); 30 | } 31 | 32 | LRESULT OnGetTextLength(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { 33 | if (wParam >= m_Text.size()) 34 | return SBT_OWNERDRAW; 35 | 36 | return (WORD)m_Text[wParam].length() | SBT_OWNERDRAW; 37 | } 38 | 39 | LRESULT OnGetText(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { 40 | if (wParam >= m_Text.size()) { 41 | *(PWSTR)lParam = 0; 42 | return 0; 43 | } 44 | wcscpy_s((PWSTR)lParam, m_Text[wParam].length() + 1, m_Text[wParam].c_str()); 45 | return 0; 46 | } 47 | 48 | LRESULT OnSetText(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { 49 | auto pane = LOBYTE(wParam); 50 | bool simple = pane == SB_SIMPLEID; 51 | if (simple) { 52 | wParam = pane = 0; 53 | } 54 | m_Text.resize(pane + 1); 55 | m_Text[pane] = (PCWSTR)lParam; 56 | DefWindowProc(uMsg, wParam | SBT_OWNERDRAW, lParam); 57 | 58 | return TRUE; 59 | } 60 | 61 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 62 | auto theme = ThemeHelper::GetCurrentTheme(); 63 | CDCHandle dc((HDC)wParam); 64 | CRect rc; 65 | GetClientRect(&rc); 66 | dc.FillSolidRect(&rc, theme->StatusBar.BackColor); 67 | //rc.left = rc.right - 20; 68 | //rc.top = rc.bottom - 20; 69 | //CSizeGrip::DrawSizeGrip(dc, rc); 70 | return 1; 71 | } 72 | 73 | LRESULT OnPaint(UINT /*uMsg*/, WPARAM, LPARAM, BOOL& bHandled) { 74 | auto theme = ThemeHelper::GetCurrentTheme(); 75 | CRect rc; 76 | GetClientRect(&rc); 77 | if (m_Text.size() == 1 && !m_Text[0].empty()) { 78 | CPaintDC dc(m_hWnd); 79 | dc.SelectFont(GetFont()); 80 | dc.SetTextColor(theme->StatusBar.TextColor); 81 | dc.SetBkMode(TRANSPARENT); 82 | rc.left += 2; 83 | dc.DrawText(m_Text[0].c_str(), -1, &rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE); 84 | rc.left -= 2; 85 | dc.SelectStockPen(DC_PEN); 86 | dc.SetDCPenColor(theme->StatusBar.TextColor); 87 | dc.MoveTo(0, 0); dc.LineTo(rc.right, 0); 88 | } 89 | else { 90 | DefWindowProc(); 91 | } 92 | return 0; 93 | } 94 | 95 | std::vector m_Text; 96 | }; 97 | 98 | class CCustomStatusBarParent : 99 | public CWindowImpl, 100 | public COwnerDraw { 101 | 102 | BEGIN_MSG_MAP(CCustomStatusBarParent) 103 | CHAIN_MSG_MAP(COwnerDraw) 104 | END_MSG_MAP() 105 | 106 | void DrawItem(LPDRAWITEMSTRUCT dis) { 107 | if (dis->hwndItem != m_sb) { 108 | SetMsgHandled(FALSE); 109 | return; 110 | } 111 | CDCHandle dc(dis->hDC); 112 | auto hIcon = m_sb.GetIcon(dis->itemID); 113 | if (hIcon) { 114 | auto pt = CRect(dis->rcItem).CenterPoint(); 115 | dc.DrawIconEx(dis->rcItem.left + 2, pt.y - 8, hIcon, 16, 16); 116 | } 117 | else { 118 | dc.SetTextColor(ThemeHelper::GetCurrentTheme()->StatusBar.TextColor); 119 | dc.SetBkMode(OPAQUE); 120 | dc.SetBkColor(ThemeHelper::GetCurrentTheme()->StatusBar.BackColor); 121 | int type; 122 | CString text; 123 | m_sb.GetText(dis->itemID, text, &type); 124 | dc.DrawText(L" " + text, -1, &dis->rcItem, DT_SINGLELINE | DT_LEFT | DT_VCENTER); 125 | } 126 | } 127 | 128 | void MeasureItem(LPMEASUREITEMSTRUCT ) { 129 | SetMsgHandled(FALSE); 130 | } 131 | 132 | void OnFinalMessage(HWND) override { 133 | delete this; 134 | } 135 | 136 | void Init(HWND hWnd, HWND hParent) { 137 | SubclassWindow(hParent); 138 | m_sb.SubclassWindow(hWnd); 139 | } 140 | 141 | CCustomStatusBar m_sb; 142 | }; 143 | 144 | -------------------------------------------------------------------------------- /BasicDemo/ProcessTreeListView.cpp: -------------------------------------------------------------------------------- 1 | // View.cpp : implementation of the CProcessTreeListView class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #include "pch.h" 6 | #include "resource.h" 7 | #include "ProcessTreeListView.h" 8 | 9 | BOOL CProcessTreeListView::PreTranslateMessage(MSG* pMsg) { 10 | pMsg; 11 | return FALSE; 12 | } 13 | 14 | void CProcessTreeListView::OnFinalMessage(HWND /*hWnd*/) { 15 | delete this; 16 | } 17 | 18 | LRESULT CProcessTreeListView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { 19 | m_hWndClient = m_TreeList.Create(m_hWnd, rcDefault, nullptr, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); 20 | m_TreeList.InsertColumn(0, L"Process Name", 220, LVCFMT_LEFT); 21 | m_TreeList.InsertColumn(1, L"PID", 100, LVCFMT_RIGHT); 22 | m_TreeList.InsertColumn(2, L"Threads", 100, LVCFMT_RIGHT); 23 | m_TreeList.InsertColumn(3, L"User name", 180, LVCFMT_LEFT); 24 | m_TreeList.InsertColumn(4, L"Image Path", 220, LVCFMT_LEFT); 25 | m_TreeList.InsertColumn(5, L"Command Line", 220, LVCFMT_LEFT); 26 | m_TreeList.InsertColumn(6, L"Working Set", 240, LVCFMT_RIGHT); 27 | 28 | Refresh(); 29 | 30 | return 0; 31 | } 32 | 33 | void CProcessTreeListView::Refresh() { 34 | auto hSnapShot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 35 | PROCESSENTRY32 pe; 36 | pe.dwSize = sizeof(pe); 37 | 38 | ::Process32First(hSnapShot, &pe); 39 | m_TreeList.SetRedraw(FALSE); 40 | HTREEITEM hParent = TVI_ROOT; 41 | int i = 0; 42 | do { 43 | auto hItem = m_TreeList.InsertItem(pe.szExeFile, hParent, TVI_LAST); 44 | ProcessInfo pi; 45 | pi.Name = pe.szExeFile; 46 | pi.Id = pe.th32ProcessID; 47 | pi.Threads = pe.cntThreads; 48 | if (++i % 4 == 0) 49 | hParent = hItem; 50 | else if (i % 6 == 0) 51 | hParent = TVI_ROOT; 52 | 53 | m_Processes.insert({ hItem, pi }); 54 | } while (::Process32Next(hSnapShot, &pe)); 55 | m_TreeList.SetRedraw(TRUE); 56 | 57 | ::CloseHandle(hSnapShot); 58 | } 59 | 60 | LRESULT CProcessTreeListView::OnItemChanged(int, LPNMHDR, BOOL&) { 61 | return LRESULT(); 62 | } 63 | 64 | LRESULT CProcessTreeListView::OnTreeGetDispInfo(int, LPNMHDR hdr, BOOL&) { 65 | auto di = (TLNMDISPINFO*)hdr; 66 | ATLASSERT(m_Processes.find(di->hItem) != m_Processes.end()); 67 | 68 | auto& pi = m_Processes[di->hItem]; 69 | CString text; 70 | switch (di->column) { 71 | case 1: 72 | text.Format(L"%u", pi.Id); 73 | break; 74 | 75 | case 2: 76 | text.Format(L"%u", pi.Threads); 77 | break; 78 | 79 | case 3: 80 | text = L"NT_AUTHORITY\\LocalSystem"; // dummy value 81 | break; 82 | default: 83 | text.Format(L"Some data at item 0x%p\n", di->hItem); 84 | break; 85 | } 86 | ::StringCchCopy(di->text, di->cchText, text); 87 | return 0; 88 | } 89 | 90 | LRESULT CProcessTreeListView::OnTreeListGetDispInfo(int, LPNMHDR, BOOL&) { 91 | return LRESULT(); 92 | } 93 | 94 | LRESULT CProcessTreeListView::OnHeaderItemClick(int, LPNMHDR hdr, BOOL&) { 95 | // 96 | // update sorting column 97 | // 98 | auto hi = (NMHEADER*)hdr; 99 | int sortColumn = (int)hi->iButton; 100 | int col = hi->iItem; 101 | 102 | bool isTree = m_TreeList.IsTreeMode(); 103 | 104 | if (col > 0) { 105 | // 106 | // switch to list mode (if not already there) 107 | // 108 | m_TreeList.SetTreeMode(false); 109 | 110 | if (m_SortColumn == -1) { 111 | m_SortColumn = sortColumn; 112 | m_SortAscending = true; 113 | } 114 | else { 115 | if (m_SortColumn == sortColumn) 116 | m_SortAscending = !m_SortAscending; 117 | else { 118 | m_SortColumn = sortColumn; 119 | m_SortAscending = true; 120 | } 121 | } 122 | if (isTree) { 123 | m_TreeList.DeleteAllItems(); 124 | Refresh(); 125 | } 126 | else { 127 | DoSort(true); 128 | } 129 | m_TreeList.SetSortColumn(col, m_SortAscending); 130 | } 131 | else { 132 | if (m_TreeList.IsTreeMode()) { 133 | // switch to list mode, sort by process name 134 | m_TreeList.SetTreeMode(false); 135 | m_SortColumn = 0; 136 | m_SortAscending = true; 137 | m_TreeList.SetSortColumn(col, m_SortAscending); 138 | BuildProcessList(true, true); 139 | } 140 | else { 141 | if (m_SortColumn == 0) { 142 | if (m_SortAscending) { 143 | m_SortAscending = false; 144 | m_TreeList.SetSortColumn(col, m_SortAscending); 145 | DoSort(true); 146 | } 147 | else { 148 | m_TreeList.ClearSortColumn(); 149 | m_TreeList.SetTreeMode(true); 150 | BuildProcessTree(); 151 | } 152 | } 153 | else { 154 | m_SortColumn = sortColumn; 155 | m_SortAscending = true; 156 | m_TreeList.SetSortColumn(col, m_SortAscending); 157 | DoSort(true); 158 | } 159 | } 160 | } 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /WTLHelper/CustomTabView.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CustomTabView.h" 3 | #include "ThemeHelper.h" 4 | #include "Theme.h" 5 | 6 | LRESULT CCustomTabView::OnEraseBkgnd(UINT, WPARAM wp, LPARAM, BOOL& handled) const { 7 | if (GetPageCount() > 0) { 8 | handled = FALSE; 9 | return 0; 10 | } 11 | CDCHandle dc((HDC)wp); 12 | CRect rc; 13 | GetClientRect(&rc); 14 | dc.FillRect(&rc, ThemeHelper::GetCurrentTheme()->GetSysBrush(COLOR_WINDOW)); 15 | 16 | return 1; 17 | } 18 | 19 | LRESULT CCustomTabView::OnUpdateTheme(UINT /*uMsg*/, WPARAM wp, LPARAM lParam, BOOL& /*bHandled*/) { 20 | Invalidate(); 21 | return 0; 22 | } 23 | 24 | bool CCustomTabView::CreateTabControl() { 25 | m_tab.Create(this->m_hWnd, this->rcDefault, nullptr, 26 | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | TCS_HOTTRACK | TCS_OWNERDRAWFIXED | TCS_TOOLTIPS, 27 | 0, m_nTabID); 28 | ATLASSERT(m_tab.m_hWnd != NULL); 29 | if (m_tab.m_hWnd == NULL) 30 | return false; 31 | 32 | m_tab.SetFont(AtlCreateControlFont()); 33 | m_bInternalFont = true; 34 | 35 | m_tab.SetItemExtra(sizeof(TABVIEWPAGE)); 36 | 37 | m_cyTabHeight = 0; 38 | 39 | return true; 40 | } 41 | 42 | void CCustomTabView::UpdateLayout() { 43 | if (!m_Redraw) 44 | return; 45 | 46 | if (GetPageCount() == 0) 47 | return; 48 | 49 | if (m_cyTabHeight == 0) { 50 | CRect item; 51 | m_tab.GetItemRect(0, &item); 52 | m_cyTabHeight = item.Height(); 53 | } 54 | 55 | RECT rect{}; 56 | this->GetClientRect(&rect); 57 | 58 | int cyOffset = 0; 59 | if (m_tab.IsWindow() && (m_tab.GetStyle() & WS_VISIBLE)) { 60 | int rows = 1; 61 | m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight * rows + 4, SWP_NOZORDER); 62 | cyOffset = m_cyTabHeight * rows + 4; 63 | } 64 | 65 | if (m_nActivePage != -1) 66 | ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, cyOffset, rect.right - rect.left, rect.bottom - rect.top - cyOffset, SWP_NOZORDER); 67 | } 68 | 69 | void CCustomTabView::SetRedraw(bool redraw) { 70 | m_tab.SetRedraw(redraw); 71 | m_Redraw = redraw; 72 | } 73 | 74 | void CCustomTabView::UpdateMenu() { 75 | if (m_menu.m_hMenu) 76 | BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem); 77 | } 78 | 79 | void CCustomTabView::BuildWindowMenu(HMENU hMenu, int nMenuItemsCount, bool bEmptyMenuItem, bool bWindowsMenuItem, bool bActivePageMenuItem, bool bActiveAsDefaultMenuItem) { 80 | ATLASSERT(::IsWindow(this->m_hWnd)); 81 | 82 | CMenuHandle menu = hMenu; 83 | int nFirstPos = 2; 84 | 85 | // Find first menu item in our range 86 | for (nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++) { 87 | UINT nID = menu.GetMenuItemID(nFirstPos); 88 | if (((nID >= ID_WINDOW_TABFIRST) && (nID <= ID_WINDOW_TABLAST)) || (nID == ID_WINDOW_SHOWTABLIST)) 89 | break; 90 | } 91 | 92 | BOOL bRet = TRUE; 93 | while (bRet) 94 | bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION); 95 | 96 | // Add separator if it's not already there 97 | int nPageCount = GetPageCount(); 98 | if ((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0)) { 99 | CMenuItemInfo mii; 100 | mii.fMask = MIIM_TYPE; 101 | menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii); 102 | if ((mii.fType & MFT_SEPARATOR) == 0) { 103 | menu.AppendMenu(MF_SEPARATOR); 104 | nFirstPos++; 105 | } 106 | } 107 | 108 | if (nPageCount > 0) { 109 | // Append menu items for all pages 110 | nMenuItemsCount = __min(__min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax); 111 | ATLASSERT(nMenuItemsCount < 100); // 2 digits only 112 | if (nMenuItemsCount >= 100) 113 | nMenuItemsCount = 99; 114 | 115 | for (int i = 0; i < nMenuItemsCount; i++) { 116 | auto title = GetPageTitle(i); 117 | menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, L" " + CString(title)); 118 | } 119 | 120 | // Mark active page 121 | if (bActivePageMenuItem && (m_nActivePage != -1)) { 122 | if (bActiveAsDefaultMenuItem) { 123 | menu.SetMenuDefaultItem((UINT)-1, TRUE); 124 | menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE); 125 | } 126 | else { 127 | menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION); 128 | } 129 | } 130 | } 131 | else { 132 | if (bEmptyMenuItem) { 133 | menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, GetEmptyListText()); 134 | menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED); 135 | } 136 | 137 | // Remove separator if nothing else is there 138 | if (!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0)) { 139 | CMenuItemInfo mii; 140 | mii.fMask = MIIM_TYPE; 141 | menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii); 142 | if ((mii.fType & MFT_SEPARATOR) != 0) 143 | menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION); 144 | } 145 | } 146 | 147 | if (bWindowsMenuItem) 148 | menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, L"Tabs..."); 149 | GetParent().SendMessage(WM_WINDOW_MENU_BUILT, (WPARAM)menu.m_hMenu); 150 | } 151 | -------------------------------------------------------------------------------- /WTLHelper/ColumnManager.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ColumnManager.h" 3 | #include 4 | 5 | class ListCtrlHelper { 6 | public: 7 | template 8 | static T FindColumn(CHeaderCtrl header, int id) { 9 | auto count = header.GetItemCount(); 10 | HDITEM hdi; 11 | hdi.mask = HDI_LPARAM; 12 | for (int i = 0; i < count; i++) { 13 | header.GetItem(i, &hdi); 14 | if (hdi.lParam == id) 15 | return static_cast(i); 16 | } 17 | return static_cast(-1); 18 | } 19 | 20 | template 21 | static T FindColumn(CListViewCtrl list, int id) { 22 | return FindColumn(list.GetHeader(), id); 23 | } 24 | 25 | template 26 | static T GetRealColumn(CListViewCtrl& list, int index) { 27 | return static_cast(GetRealColumn(list.GetHeader(), index)); 28 | } 29 | 30 | template 31 | static T GetRealColumn(CHeaderCtrl header, int index) { 32 | HDITEM hdi; 33 | hdi.mask = HDI_LPARAM; 34 | header.GetItem(index, &hdi); 35 | return static_cast(hdi.lParam); 36 | } 37 | }; 38 | 39 | HWND ColumnManager::Attach(HWND hListView) { 40 | auto h = m_ListView; 41 | m_ListView = hListView; 42 | return h; 43 | } 44 | 45 | ColumnManager::~ColumnManager() = default; 46 | 47 | bool ColumnManager::CopyTo(ColumnManager & other) const { 48 | if (other.GetCount() != GetCount()) 49 | return false; 50 | 51 | int i = 0; 52 | for (const auto& column : m_Columns) { 53 | other.SetColumn(i, column); 54 | i++; 55 | } 56 | return true; 57 | } 58 | 59 | void ColumnManager::AddFromControl(HWND hWnd) { 60 | CHeaderCtrl header(hWnd == nullptr ? m_ListView.GetHeader() : CListViewCtrl(hWnd).GetHeader()); 61 | ATLASSERT(header); 62 | auto count = header.GetItemCount(); 63 | HDITEM item; 64 | WCHAR text[64]; 65 | item.pszText = text; 66 | item.cchTextMax = _countof(text); 67 | item.mask = HDI_FORMAT | HDI_WIDTH | HDI_TEXT | HDI_LPARAM; 68 | for (int i = 0; i < count; i++) { 69 | ATLVERIFY(header.GetItem(i, &item)); 70 | ColumnInfo info; 71 | info.DefaultWidth = item.cxy; 72 | info.Flags = (info.DefaultWidth <= 1 && ((item.fmt & HDF_FIXEDWIDTH) > 0) ? ColumnFlags::Visible : ColumnFlags::None); 73 | info.Name = item.pszText; 74 | info.Tag = (int)item.lParam; 75 | m_Columns.push_back(info); 76 | } 77 | } 78 | 79 | void ColumnManager::SetVisible(int column, bool visible) { 80 | m_Columns[column].SetVisible(visible); 81 | } 82 | 83 | bool ColumnManager::IsVisible(int column) const { 84 | ATLASSERT(column >= 0 && column < GetCount()); 85 | return (m_Columns[column].Flags & ColumnFlags::Visible) == ColumnFlags::Visible; 86 | } 87 | 88 | bool ColumnManager::IsModified(int column) const { 89 | return (GetColumn(column).Flags & ColumnFlags::Modified) == ColumnFlags::Modified; 90 | } 91 | 92 | void ColumnManager::SetModified(int column, bool modified) { 93 | auto& col = m_Columns[column]; 94 | if (modified) 95 | col.Flags |= ColumnFlags::Modified; 96 | else 97 | col.Flags &= ~ColumnFlags::Modified; 98 | } 99 | 100 | bool ColumnManager::IsConst(int column) const { 101 | return (m_Columns[column].Flags & ColumnFlags::Const) == ColumnFlags::Const; 102 | } 103 | 104 | void ColumnManager::Clear() { 105 | m_Columns.clear(); 106 | if (m_ListView) 107 | while (m_ListView.DeleteColumn(0)) 108 | ; 109 | } 110 | 111 | void ColumnManager::UpdateColumns() { 112 | if (GetCount() == 0) { 113 | Clear(); 114 | return; 115 | } 116 | auto header = m_ListView.GetHeader(); 117 | HDITEM hdi; 118 | hdi.mask = HDI_LPARAM; 119 | for (int i = 0; i < GetCount(); i++) { 120 | if (IsModified(i)) { 121 | if (IsVisible(i)) { 122 | auto& info = GetColumn(i); 123 | // make visible - add column 124 | int c = m_ListView.InsertColumn(header.GetItemCount(), info.Name, info.Format, info.DefaultWidth); 125 | hdi.lParam = i; 126 | header.SetItem(c, &hdi); 127 | } 128 | else { 129 | int c = ListCtrlHelper::FindColumn(header, i); 130 | ATLASSERT(c >= 0); 131 | m_ListView.DeleteColumn(c); 132 | } 133 | SetModified(i, false); 134 | } 135 | } 136 | } 137 | 138 | const ColumnManager::ColumnInfo& ColumnManager::GetColumn(int index) const { 139 | return m_Columns[index]; 140 | } 141 | 142 | const std::vector& ColumnManager::GetColumnsByCategory(PCWSTR category) const { 143 | return m_ColumnsByCategory.at(category); 144 | } 145 | 146 | const std::vector& ColumnManager::GetCategories() const { 147 | return m_Categories; 148 | } 149 | 150 | void ColumnManager::SetColumn(int i, const ColumnInfo & info) { 151 | ATLASSERT(i >= 0 && i < GetCount()); 152 | if ((info.Flags & ColumnFlags::Visible) != (m_Columns[i].Flags & ColumnFlags::Visible)) { 153 | SetVisible(i, (info.Flags & ColumnFlags::Visible) == ColumnFlags::Visible); 154 | } 155 | } 156 | 157 | int ColumnManager::GetRealColumn(int index) const { 158 | HDITEM hdi; 159 | hdi.mask = HDI_LPARAM; 160 | m_ListView.GetHeader().GetItem(index, &hdi); 161 | return static_cast(hdi.lParam); 162 | } 163 | 164 | bool ColumnManager::DeleteColumn(int col) { 165 | if (col >= (int)m_Columns.size() || col < 0) 166 | return false; 167 | 168 | m_Columns.erase(m_Columns.begin() + col); 169 | m_ListView.DeleteColumn(0); 170 | HDITEM hdi; 171 | hdi.mask = HDI_LPARAM; 172 | auto header = m_ListView.GetHeader(); 173 | for (auto i = col; i < (int)m_Columns.size(); i++) { 174 | header.GetItem(i, &hdi); 175 | hdi.lParam--; 176 | header.SetItem(i, &hdi); 177 | } 178 | return true; 179 | } 180 | -------------------------------------------------------------------------------- /WTLHelper/Settings.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Settings.h" 3 | #include "IniFile.h" 4 | 5 | bool Settings::LoadFromKey(PCWSTR registryPath) { 6 | if (registryPath == nullptr) 7 | registryPath = m_path.c_str(); 8 | else 9 | m_path = registryPath; 10 | ATLASSERT(registryPath); 11 | if (registryPath == nullptr) 12 | return false; 13 | 14 | CRegKey key; 15 | if (ERROR_SUCCESS != key.Open(HKEY_CURRENT_USER, registryPath)) 16 | return false; 17 | 18 | WCHAR name[128]; 19 | auto size = 1 << 16; 20 | auto value = std::make_unique(size); 21 | DWORD type; 22 | for (DWORD i = 0;; ++i) { 23 | DWORD lname = _countof(name), lvalue = size; 24 | auto error = ::RegEnumValue(key, i, name, &lname, nullptr, &type, value.get(), &lvalue); 25 | if (ERROR_NO_MORE_ITEMS == error) 26 | break; 27 | 28 | if (error != ERROR_SUCCESS) 29 | continue; 30 | 31 | auto it = m_settings.find(name); 32 | if (it == m_settings.end()) 33 | m_settings.insert({ name, Setting(name, value.get(), lvalue, (SettingType)type) }); 34 | else 35 | it->second.Set(value.get(), lvalue); 36 | } 37 | return true; 38 | } 39 | 40 | bool Settings::SaveToKey(PCWSTR registryPath) const { 41 | if (registryPath == nullptr) 42 | registryPath = m_path.c_str(); 43 | 44 | ATLASSERT(registryPath); 45 | if (registryPath == nullptr) 46 | return false; 47 | 48 | CRegKey key; 49 | key.Create(HKEY_CURRENT_USER, registryPath, nullptr, 0, KEY_WRITE); 50 | if (!key) 51 | return false; 52 | 53 | for (auto& [name, setting] : m_settings) { 54 | key.SetValue(name.c_str(), (DWORD)setting.Type, setting.Buffer.get(), setting.Size); 55 | } 56 | return true; 57 | } 58 | 59 | bool Settings::LoadFromFile(PCWSTR path) { 60 | if (path == nullptr) 61 | path = m_path.c_str(); 62 | 63 | ATLASSERT(path); 64 | if (path == nullptr) 65 | return false; 66 | else 67 | m_path = path; 68 | 69 | IniFile file(path); 70 | if (!file.IsValid()) 71 | return false; 72 | 73 | PCWSTR section = L"General"; 74 | for (auto& [name, setting] : m_settings) { 75 | switch (setting.Type) { 76 | case SettingType::String: 77 | setting.SetString(file.ReadString(section, name.c_str())); 78 | break; 79 | 80 | case SettingType::Int32: 81 | setting.Set(file.ReadInt(section, name.c_str())); 82 | break; 83 | 84 | default: 85 | unsigned size; 86 | auto data = file.ReadBinary(section, name.c_str(), size); 87 | if (data && size > 0) 88 | setting.Set(data.get(), size); 89 | break; 90 | } 91 | } 92 | 93 | return true; 94 | } 95 | 96 | bool Settings::SaveToFile(PCWSTR path) const { 97 | if (path == nullptr) 98 | path = m_path.c_str(); 99 | 100 | ATLASSERT(path); 101 | if (path == nullptr) 102 | return false; 103 | 104 | IniFile file(path); 105 | 106 | PCWSTR section = L"General"; 107 | for (auto& [name, setting] : m_settings) { 108 | switch (setting.Type) { 109 | case SettingType::String: 110 | file.WriteString(section, name.c_str(), (PCWSTR)setting.Buffer.get()); 111 | break; 112 | 113 | case SettingType::Int32: 114 | file.WriteInt(section, name.c_str(), *(DWORD*)setting.Buffer.get()); 115 | break; 116 | 117 | default: 118 | file.WriteBinary(section, name.c_str(), setting.Buffer.get(), setting.Size); 119 | break; 120 | } 121 | } 122 | return true; 123 | } 124 | 125 | bool Settings::Load(PCWSTR path) { 126 | WCHAR fullpath[MAX_PATH]; 127 | ::GetModuleFileName(nullptr, fullpath, _countof(fullpath)); 128 | auto dot = wcsrchr(fullpath, L'.'); 129 | ATLASSERT(dot); 130 | if (!dot) 131 | return false; 132 | 133 | *dot = 0; 134 | wcscat_s(fullpath, L".ini"); 135 | 136 | if (::GetFileAttributes(fullpath) == INVALID_FILE_ATTRIBUTES) { 137 | // 138 | // ini file does not exist, use Registry 139 | // 140 | return LoadFromKey(path); 141 | } 142 | return LoadFromFile(fullpath); 143 | } 144 | 145 | bool Settings::Save() const { 146 | if (m_path.empty()) 147 | return false; 148 | 149 | return m_path[1] == L':' ? SaveToFile() : SaveToKey(); 150 | } 151 | 152 | void Settings::Set(PCWSTR name, int value) { 153 | return Set(name, value, SettingType::Int32); 154 | } 155 | 156 | void Settings::Set(PCWSTR name, std::vector const& values) { 157 | Setting s(name, values); 158 | m_settings.erase(name); 159 | m_settings.insert({ name, std::move(s) }); 160 | } 161 | 162 | void Settings::SetString(PCWSTR name, PCWSTR value) { 163 | auto it = m_settings.find(name); 164 | if (it != m_settings.end()) { 165 | it->second.SetString(value); 166 | } 167 | else { 168 | Setting s(name, value); 169 | m_settings.insert({ name, std::move(s) }); 170 | } 171 | } 172 | 173 | bool Settings::SaveWindowPosition(HWND hWnd, PCWSTR name) { 174 | CWindow win(hWnd); 175 | WINDOWPLACEMENT wp = { sizeof(wp) }; 176 | if (!win.GetWindowPlacement(&wp)) 177 | return false; 178 | 179 | Set(name, wp); 180 | return true; 181 | } 182 | 183 | bool Settings::LoadWindowPosition(HWND hWnd, PCWSTR name) const { 184 | const auto wp = GetBinary(name); 185 | if (wp == nullptr) 186 | return false; 187 | 188 | return CWindow(hWnd).SetWindowPlacement(wp); 189 | } 190 | 191 | std::wstring Settings::GetString(PCWSTR name) const { 192 | auto it = m_settings.find(name); 193 | if (it == m_settings.end()) 194 | return L""; 195 | return (PCWSTR)it->second.Buffer.get(); 196 | } 197 | 198 | int Settings::GetInt32(PCWSTR name) const { 199 | return GetValue(name); 200 | } 201 | 202 | void Setting::SetString(PCWSTR value) { 203 | Buffer = std::make_unique(Size = (1 + (int)::wcslen(value)) * sizeof(wchar_t)); 204 | ::memcpy(Buffer.get(), value, Size); 205 | Type = SettingType::String; 206 | } 207 | -------------------------------------------------------------------------------- /WTLHelper/CustomHeader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Theme.h" 4 | #include "ThemeHelper.h" 5 | 6 | class CCustomHeader : 7 | public CWindowImpl { 8 | public: 9 | BEGIN_MSG_MAP(CCustomHeader) 10 | MESSAGE_HANDLER(WM_PAINT, OnPaint) 11 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 12 | END_MSG_MAP() 13 | 14 | LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 15 | if (ThemeHelper::IsDefault()) { 16 | bHandled = FALSE; 17 | return 0; 18 | } 19 | CPaintDC dc(m_hWnd); 20 | if (GetItemCount()) { 21 | CRect rc; 22 | HDITEM hdi{ HDI_TEXT | HDI_FORMAT }; 23 | WCHAR text[64]; 24 | hdi.pszText = text; 25 | hdi.cchTextMax = std::size(text); 26 | dc.SetTextColor(ThemeHelper::GetCurrentTheme()->TextColor); 27 | dc.SetBkMode(TRANSPARENT); 28 | dc.SelectFont(GetFont()); 29 | dc.SelectStockPen(DC_PEN); 30 | dc.SetDCPenColor(RGB(192, 192, 192)); 31 | std::vector order(GetItemCount()); 32 | GetOrderArray(GetItemCount(), order.data()); 33 | const int sortLen = 4; 34 | for(auto index : order) { 35 | GetItemRect(index, &rc); 36 | GetItem(index, &hdi); 37 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 38 | rc.right -= 6; 39 | rc.left += 6; 40 | auto fmt = DT_SINGLELINE | DT_LEFT | DT_VCENTER; 41 | if ((hdi.fmt & HDF_JUSTIFYMASK) == HDF_CENTER) 42 | fmt |= DT_CENTER; 43 | else if ((hdi.fmt & HDF_JUSTIFYMASK) == HDF_RIGHT) 44 | fmt |= DT_RIGHT; 45 | 46 | dc.DrawText(text, -1, &rc, fmt); 47 | rc.top += 2; 48 | if (hdi.fmt & HDF_SORTUP) { 49 | dc.MoveTo(rc.CenterPoint().x, rc.top); 50 | dc.LineTo(rc.CenterPoint().x - sortLen, rc.top + sortLen); 51 | dc.MoveTo(rc.CenterPoint().x, rc.top); 52 | dc.LineTo(rc.CenterPoint().x + sortLen, rc.top + sortLen); 53 | } 54 | else if (hdi.fmt & HDF_SORTDOWN) { 55 | dc.MoveTo(rc.CenterPoint().x - sortLen, rc.top); 56 | dc.LineTo(rc.CenterPoint().x, rc.top + sortLen); 57 | dc.MoveTo(rc.CenterPoint().x + sortLen, rc.top); 58 | dc.LineTo(rc.CenterPoint().x, rc.top + sortLen); 59 | } 60 | rc.top -= 2; 61 | rc.right += 2; 62 | dc.MoveTo(rc.right, rc.top); 63 | dc.LineTo(rc.right, rc.bottom); 64 | } 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { 71 | if (ThemeHelper::IsDefault()) { 72 | bHandled = FALSE; 73 | return 0; 74 | } 75 | DefWindowProc(); 76 | CClientDC dc(*this); 77 | CRect rc; 78 | GetClientRect(&rc); 79 | RECT rcItem; 80 | if (GetItemCount()) { 81 | std::vector order(GetItemCount()); 82 | GetOrderArray(GetItemCount(), order.data()); 83 | GetItemRect(order.back(), &rcItem); 84 | rc.left = rcItem.right; 85 | if (rc.right > rc.left) 86 | dc.FillSolidRect(&rc, ThemeHelper::GetCurrentTheme()->BackColor); 87 | } 88 | return 1; 89 | } 90 | 91 | int m_Width{ 0 }; 92 | }; 93 | 94 | class CCustomHeaderParent : 95 | public CWindowImpl, 96 | public CCustomDraw { 97 | public: 98 | BEGIN_MSG_MAP(CCustomHeaderParent) 99 | //CHAIN_MSG_MAP(CCustomDraw) 100 | END_MSG_MAP() 101 | 102 | void Init(HWND hWnd) { 103 | m_Header.SubclassWindow(hWnd); 104 | } 105 | 106 | void OnFinalMessage(HWND) override { 107 | m_Header.Detach(); 108 | delete this; 109 | } 110 | 111 | DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW cd) { 112 | if (cd->hdr.hwndFrom != m_Header) { 113 | SetMsgHandled(FALSE); 114 | return CDRF_DODEFAULT; 115 | } 116 | 117 | return CDRF_NOTIFYITEMDRAW; 118 | } 119 | 120 | DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW cd) { 121 | if (cd->hdr.hwndFrom != m_Header) { 122 | SetMsgHandled(FALSE); 123 | return CDRF_DODEFAULT; 124 | } 125 | 126 | HDITEM item; 127 | item.mask = HDI_TEXT | HDI_FORMAT; 128 | WCHAR text[64]; 129 | item.pszText = text; 130 | item.cchTextMax = _countof(text); 131 | ATLVERIFY(m_Header.GetItem((int)cd->dwItemSpec, &item)); 132 | 133 | CDCHandle dc(cd->hdc); 134 | dc.SelectStockPen(WHITE_PEN); 135 | CRect rc(cd->rc); 136 | rc.bottom -= 2; 137 | bool isSortinColumn = item.fmt & (HDF_SORTUP | HDF_SORTDOWN); 138 | auto color = ThemeHelper::GetCurrentTheme()->BackColor; 139 | dc.FillSolidRect(&rc, isSortinColumn ? RGB(32, 0, 0) : color); 140 | 141 | if (cd->dwItemSpec != 0) { 142 | CPen pen; 143 | pen.CreatePen(PS_SOLID, 1, RGB(64, 64, 64)); 144 | auto hOldPen = dc.SelectPen(pen); 145 | dc.MoveTo(rc.left, rc.top); 146 | dc.LineTo(rc.left, rc.bottom); 147 | dc.SelectPen(hOldPen); 148 | } 149 | dc.MoveTo(rc.right, rc.top); 150 | dc.LineTo(rc.right, rc.bottom); 151 | 152 | dc.SetBkMode(TRANSPARENT); 153 | dc.SetTextColor(ThemeHelper::GetCurrentTheme()->TextColor); 154 | rc = cd->rc; 155 | rc.left += 6; 156 | 157 | DWORD fmt = DT_LEFT; 158 | if ((item.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT) 159 | fmt = DT_RIGHT; 160 | else if ((item.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER) 161 | fmt = DT_CENTER; 162 | 163 | rc.DeflateRect(4, 0); 164 | dc.DrawText(text, -1, &rc, fmt | DT_VCENTER | DT_SINGLELINE); 165 | 166 | // draw sort indicator (if any) 167 | if (item.fmt & (HDF_SORTUP | HDF_SORTDOWN)) { 168 | auto pt = rc.CenterPoint(); 169 | auto up = item.fmt & HDF_SORTUP; 170 | pt.y = up ? rc.top : rc.top + 4; 171 | int offset = up ? 4 : -4; 172 | dc.MoveTo(pt); 173 | dc.LineTo(pt.x - offset, pt.y + offset); 174 | dc.MoveTo(pt); 175 | dc.LineTo(pt.x + offset, pt.y + offset); 176 | } 177 | return CDRF_NOTIFYPOSTPAINT; 178 | } 179 | 180 | CCustomHeader m_Header; 181 | }; 182 | -------------------------------------------------------------------------------- /WTLHelper/TreeViewHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | struct CTreeViewHelper { 5 | template 6 | HTREEITEM InsertTreeItem(CTreeViewCtrl& tree, PCWSTR text, TIcon image, TIcon selectedImage, TData const& data, HTREEITEM hParent = TVI_ROOT, HTREEITEM hAfter = TVI_LAST) { 7 | auto hItem = tree.InsertItem(text, static_cast(image), static_cast(selectedImage), hParent, hAfter); 8 | if(hItem) 9 | tree.SetItemData(hItem, static_cast(data)); 10 | return hItem; 11 | } 12 | 13 | template 14 | HTREEITEM InsertTreeItem(CTreeViewCtrl& tree, PCWSTR text, TIcon image, TData const& data, HTREEITEM hParent = TVI_ROOT, HTREEITEM hAfter = TVI_LAST) { 15 | return InsertTreeItem(tree, text, static_cast(image), static_cast(image), data, hParent, hAfter); 16 | } 17 | 18 | template 19 | static TData GetItemData(CTreeViewCtrl const& tree, HTREEITEM hItem) { 20 | return static_cast(tree.GetItemData(hItem)); 21 | } 22 | 23 | template 24 | static void SetItemData(CTreeViewCtrl& tree, HTREEITEM hItem, TData const& data) { 25 | tree.SetItemData(hItem, static_cast(data)); 26 | } 27 | 28 | static HTREEITEM FindItem(CTreeViewCtrl& tree, HTREEITEM hParent, PCWSTR path) { 29 | int start = 0; 30 | CString spath(path); 31 | if (spath[0] == L'\\') { 32 | // skip first 33 | spath = spath.Mid(spath.Find(L'\\', 1)); 34 | } 35 | HTREEITEM hItem = nullptr; 36 | while (hParent) { 37 | auto name = spath.Tokenize(L"\\", start); 38 | if (name.IsEmpty()) 39 | break; 40 | tree.Expand(hParent, TVE_EXPAND); 41 | hItem = FindChild(tree, hParent, name); 42 | if (!hItem) 43 | break; 44 | hParent = hItem; 45 | } 46 | return hItem; 47 | } 48 | 49 | static HTREEITEM FindChild(CTreeViewCtrl& tree, HTREEITEM item, PCWSTR name) { 50 | item = tree.GetChildItem(item); 51 | while (item) { 52 | CString text; 53 | tree.GetItemText(item, text); 54 | if (text.CompareNoCase(name) == 0) 55 | return item; 56 | item = tree.GetNextSiblingItem(item); 57 | } 58 | return nullptr; 59 | } 60 | 61 | template 62 | static HTREEITEM FindChildByData(CTreeViewCtrl const& tree, HTREEITEM item, TData const& data) { 63 | item = tree.GetChildItem(item); 64 | while (item) { 65 | if (GetItemData(tree, item) == data) 66 | return item; 67 | 68 | auto item2 = FindChildByData(tree, item, data); 69 | if (item2) 70 | return item2; 71 | item = tree.GetNextSiblingItem(item); 72 | } 73 | return nullptr; 74 | } 75 | 76 | template 77 | HTREEITEM FindItemByData(CTreeViewCtrl const& tree, HTREEITEM hParent, TData const& data) const { 78 | int start = 0; 79 | HTREEITEM hItem = nullptr; 80 | while (hParent) { 81 | hItem = FindChildByData(tree, hParent, data); 82 | if (hItem) 83 | break; 84 | hParent = tree.GetNextSiblingItem(hParent); 85 | } 86 | return hItem; 87 | } 88 | 89 | static CString GetFullItemPath(CTreeViewCtrl const& tree, HTREEITEM hItem) { 90 | CString path; 91 | while (hItem) { 92 | CString name; 93 | tree.GetItemText(hItem, name); 94 | path = name + (path.IsEmpty() ? CString() : (L"\\" + path)); 95 | hItem = tree.GetParentItem(hItem); 96 | } 97 | return path.TrimRight(L'\\'); 98 | } 99 | 100 | protected: 101 | BEGIN_MSG_MAP(CTreeViewHelper) 102 | NOTIFY_CODE_HANDLER(TVN_SELCHANGED, OnSelChanged) 103 | NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING, OnItemExpanding) 104 | NOTIFY_CODE_HANDLER(NM_RCLICK, OnRightClick) 105 | NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDoubleClick) 106 | ALT_MSG_MAP(1) 107 | REFLECTED_NOTIFY_CODE_HANDLER(TVN_SELCHANGED, OnSelChanged) 108 | REFLECTED_NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING, OnItemExpanding) 109 | REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRightClick) 110 | REFLECTED_NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDoubleClick) 111 | END_MSG_MAP() 112 | 113 | LRESULT OnItemExpanding(int /*idCtrl*/, LPNMHDR hdr, BOOL& /*bHandled*/) { 114 | auto pT = static_cast(this); 115 | auto tv = (NMTREEVIEW*)hdr; 116 | return pT->OnTreeItemExpanding(hdr->hwndFrom, tv->itemNew.hItem, tv->itemNew.state, tv->action); 117 | } 118 | 119 | LRESULT OnSelChanged(int /*idCtrl*/, LPNMHDR hdr, BOOL& /*bHandled*/) { 120 | auto pT = static_cast(this); 121 | auto tv = (NMTREEVIEW*)hdr; 122 | pT->OnTreeSelChanged(hdr->hwndFrom, tv->itemOld.hItem, tv->itemNew.hItem); 123 | return 0; 124 | } 125 | 126 | LRESULT OnRightClick(int /*idCtrl*/, LPNMHDR hdr, BOOL& /*bHandled*/) { 127 | CPoint pt; 128 | ::GetCursorPos(&pt); 129 | CPoint pt2(pt); 130 | CTreeViewCtrl tv(hdr->hwndFrom); 131 | tv.ScreenToClient(&pt); 132 | auto hItem = tv.HitTest(pt, nullptr); 133 | if (hItem) 134 | return static_cast(this)->OnTreeRightClick(tv, hItem, pt2); 135 | return 0; 136 | } 137 | 138 | LRESULT OnDoubleClick(int /*idCtrl*/, LPNMHDR hdr, BOOL& /*bHandled*/) { 139 | CPoint pt; 140 | ::GetCursorPos(&pt); 141 | CTreeViewCtrl tv(hdr->hwndFrom); 142 | tv.ScreenToClient(&pt); 143 | auto hItem = tv.HitTest(pt, nullptr); 144 | if (hItem) 145 | return static_cast(this)->OnTreeDoubleClick(tv, hItem); 146 | return 0; 147 | } 148 | 149 | private: 150 | // overridables 151 | 152 | void OnTreeSelChanged(HWND tree, HTREEITEM hOld, HTREEITEM hNew) {} 153 | bool OnTreeItemExpanding(HWND tree, HTREEITEM hItem, DWORD state, DWORD action) { 154 | return false; 155 | } 156 | 157 | bool OnTreeRightClick(HWND tree, HTREEITEM hItem, POINT const& pt) { 158 | return false; 159 | } 160 | 161 | bool OnTreeDoubleClick(HWND tree, HTREEITEM hItem) { 162 | return false; 163 | } 164 | 165 | }; 166 | -------------------------------------------------------------------------------- /WTLHelper/SortedFilteredVector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | class SortedFilteredVector { 9 | public: 10 | SortedFilteredVector(size_t capacity = 16) { 11 | reserve(capacity); 12 | } 13 | 14 | SortedFilteredVector& operator=(std::vector const& other) { 15 | m_items = other; 16 | size_t count; 17 | m_indices.resize(count = other.size()); 18 | for (size_t i = 0; i < count; i++) 19 | m_indices[i] = i; 20 | return *this; 21 | } 22 | 23 | void reserve(size_t capacity) { 24 | m_items.reserve(capacity); 25 | m_indices.reserve(capacity); 26 | } 27 | 28 | void clear() { 29 | m_items.clear(); 30 | m_indices.clear(); 31 | } 32 | 33 | bool empty() const { 34 | return m_items.empty(); 35 | } 36 | 37 | void push_back(const T& value) { 38 | m_items.push_back(value); 39 | if (m_Filter == nullptr || m_Filter(value, m_items.size() - 1)) 40 | m_indices.push_back(m_indices.size()); 41 | } 42 | 43 | void push_back(T&& value) { 44 | if (m_Filter == nullptr || m_Filter(value, m_items.size() - 1)) 45 | m_indices.push_back(m_indices.size()); 46 | m_items.push_back(std::move(value)); 47 | } 48 | 49 | void shrink_to_fit() { 50 | m_items.shrink_to_fit(); 51 | m_indices.shrink_to_fit(); 52 | } 53 | 54 | void Remove(size_t index) { 55 | auto realIndex = m_indices[index]; 56 | m_items.erase(m_items.begin() + realIndex); 57 | m_indices.erase(m_indices.begin() + index); 58 | for (size_t i = 0; i < m_indices.size(); i++) { 59 | if (m_indices[i] >= realIndex) 60 | m_indices[i]--; 61 | } 62 | } 63 | 64 | void ClearSort() { 65 | int c = 0; 66 | for (auto& i : m_indices) 67 | i = c++; 68 | } 69 | 70 | typename std::vector::const_iterator begin() const { 71 | return m_items.begin(); 72 | } 73 | 74 | typename std::vector::const_iterator end() const { 75 | return m_items.end(); 76 | } 77 | 78 | template 79 | void append(Iterator begin, Iterator end) { 80 | for (auto it = begin; it != end; ++it) 81 | push_back(std::move(*it)); 82 | } 83 | 84 | template 85 | void insert(size_t at, Iterator begin, Iterator end) { 86 | // 87 | // only call after ResetSort and no filter 88 | // 89 | size_t count = end - begin; 90 | m_items.insert(m_items.begin() + at, begin, end); 91 | std::vector indices(count); 92 | for (size_t c = 0; c < count; ++c) { 93 | indices[c] = c + at; 94 | } 95 | m_indices.insert(m_indices.begin() + at, indices.begin(), indices.end()); 96 | for (size_t c = at + count; c < m_indices.size(); c++) 97 | m_indices[c] += count; 98 | } 99 | 100 | void Set(std::vector items) { 101 | m_items = std::move(items); 102 | auto count = m_items.size(); 103 | m_indices.clear(); 104 | m_indices.reserve(count); 105 | for (decltype(count) i = 0; i < count; i++) 106 | m_indices.push_back(i); 107 | } 108 | 109 | const T& operator[](size_t index) const { 110 | return m_items[m_indices[index]]; 111 | } 112 | 113 | T& operator[](size_t index) { 114 | return m_items[m_indices[index]]; 115 | } 116 | 117 | const T& GetReal(size_t index) const { 118 | return m_items[index]; 119 | } 120 | 121 | void Sort(std::function compare) { 122 | std::sort(m_indices.begin(), m_indices.end(), [&](size_t i1, size_t i2) { 123 | return compare(m_items[i1], m_items[i2]); 124 | }); 125 | } 126 | 127 | void Sort(size_t start, size_t end, std::function compare) { 128 | if (start >= m_indices.size()) 129 | return; 130 | 131 | std::sort(m_indices.begin() + start, end == 0 ? m_indices.end() : (m_indices.begin() + end), [&](size_t i1, size_t i2) { 132 | return compare(m_items[i1], m_items[i2]); 133 | }); 134 | } 135 | 136 | size_t size() const { 137 | return m_indices.size(); 138 | } 139 | 140 | size_t TotalSize() const { 141 | return m_items.size(); 142 | } 143 | 144 | void Filter(std::function predicate, bool append = false) { 145 | m_Filter = predicate; 146 | if (!append) { 147 | m_indices.clear(); 148 | } 149 | auto count = m_items.size(); 150 | if (predicate == nullptr && !append) { 151 | for (decltype(count) i = 0; i < count; i++) 152 | m_indices.push_back(i); 153 | } 154 | else if (append) { 155 | std::vector indices2(m_indices); 156 | int j = 0; 157 | for (decltype(count) i = 0; i < m_indices.size(); i++, j++) { 158 | if (!predicate(m_items[m_indices[i]], (int)i)) { 159 | indices2.erase(indices2.begin() + j); 160 | j--; 161 | } 162 | } 163 | m_indices = std::move(indices2); 164 | } 165 | else { 166 | for (decltype(count) i = 0; i < count; i++) 167 | if (predicate(m_items[i], int(i))) 168 | m_indices.push_back(i); 169 | } 170 | } 171 | 172 | bool erase(size_t index) { 173 | if (index >= m_items.size()) 174 | return false; 175 | 176 | m_items.erase(m_items.begin() + m_indices[index]); 177 | m_indices.erase(m_indices.begin() + index); 178 | for (; index < m_indices.size(); index++) 179 | m_indices[index]--; 180 | 181 | return true; 182 | } 183 | 184 | const std::vector& GetRealAll() const { 185 | return m_items; 186 | } 187 | 188 | const std::vector GetItems() const { 189 | std::vector items; 190 | items.reserve(size()); 191 | for (size_t i = 0; i < size(); i++) 192 | items.push_back(m_items[m_indices[i]]); 193 | return items; 194 | } 195 | 196 | const std::vector GetAllItems() const { 197 | return m_items; 198 | } 199 | 200 | private: 201 | std::vector m_items; 202 | std::vector m_indices; 203 | std::function m_Filter; 204 | }; 205 | 206 | -------------------------------------------------------------------------------- /WTLHelper/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum class SettingType { 8 | String = REG_SZ, 9 | Int32 = REG_DWORD, 10 | Int64 = REG_QWORD, 11 | Binary = REG_BINARY, 12 | Bool = REG_DWORD, 13 | MultiString = REG_MULTI_SZ, 14 | }; 15 | 16 | struct Setting { 17 | std::wstring Name; 18 | SettingType Type{ SettingType::String }; 19 | std::unique_ptr Buffer; 20 | uint32_t Size; 21 | 22 | Setting(std::wstring name, const std::wstring& value) : Name(std::move(name)) { 23 | Buffer = std::make_unique(Size = (1 + (int)value.size()) * sizeof(wchar_t)); 24 | ::memcpy(Buffer.get(), value.data(), Size); 25 | } 26 | template 27 | Setting(std::wstring name, const T& value, SettingType type) : Name(std::move(name)), Type(type) { 28 | Buffer = std::make_unique(Size = sizeof(T)); 29 | ::memcpy(Buffer.get(), &value, Size); 30 | } 31 | 32 | Setting(std::wstring name, const void* value, int size, SettingType type = SettingType::Binary) : Name(std::move(name)), Type(type) { 33 | Buffer = std::make_unique(Size = size); 34 | ::memcpy(Buffer.get(), value, Size); 35 | } 36 | 37 | Setting(std::wstring name, std::vector const& value) : Name(std::move(name)), Type(SettingType::MultiString) { 38 | size_t size = sizeof(WCHAR); 39 | std::for_each(value.begin(), value.end(), [&](auto& str) { size += sizeof(WCHAR) * (1 + str.length()); }); 40 | Buffer = std::make_unique(Size = (uint32_t)size); 41 | auto p = Buffer.get(); 42 | std::for_each(value.begin(), value.end(), [&](auto& str) { 43 | auto count = (str.length() + 1) * sizeof(WCHAR); 44 | ::memcpy(p, str.c_str(), count); 45 | p += count; 46 | }); 47 | p[0] = p[1] = 0; 48 | } 49 | 50 | template 51 | void Set(const T& value) { 52 | Buffer = std::make_unique(sizeof(T)); 53 | memcpy(Buffer.get(), &value, sizeof(T)); 54 | } 55 | 56 | void SetString(PCWSTR value); 57 | 58 | void Set(const void* value, int size) { 59 | Buffer = std::make_unique(Size = size); 60 | memcpy(Buffer.get(), value, size); 61 | } 62 | }; 63 | 64 | #define BEGIN_SETTINGS(className) \ 65 | inline static className* _instance; \ 66 | className() { \ 67 | if(_instance == nullptr) _instance = this; \ 68 | InitSettings(); \ 69 | } \ 70 | static className& Get() { \ 71 | return *_instance; \ 72 | } \ 73 | void InitSettings() { \ 74 | 75 | #define END_SETTINGS } 76 | 77 | #define SETTING_STRING(name, value) m_settings.insert({ L#name, Setting(L#name, value) }) 78 | #define SETTING(name, value, type) m_settings.insert({ L#name, Setting(L#name, value, type) }) 79 | #define DEF_SETTING_STRING(name) \ 80 | std::wstring name() const { return GetString(L#name); } \ 81 | void name(const std::wstring& value) { SetString(L#name, value.c_str()); } 82 | 83 | #define DEF_SETTING(name, type) \ 84 | type name() const { return GetValueOrDefault(L#name); } \ 85 | void name(const type& value) { Set(L#name, value); } 86 | 87 | #define DEF_SETTING_REF(name, type) \ 88 | type& name() const { return GetValueRef(L#name); } 89 | 90 | #define DEF_SETTING_MULTI(name) \ 91 | std::vector name() const { return GetMultiString(L#name); } \ 92 | void name(std::vector const& value) { Set(L#name, value); } 93 | 94 | class Settings { 95 | public: 96 | Settings() = default; 97 | 98 | bool LoadFromKey(PCWSTR registryPath = nullptr); 99 | bool SaveToKey(PCWSTR registryPath = nullptr) const; 100 | bool LoadFromFile(PCWSTR path = nullptr); 101 | bool SaveToFile(PCWSTR path = nullptr) const; 102 | bool Load(PCWSTR path); 103 | bool Save() const; 104 | 105 | template 106 | void Set(const std::wstring& name, const T& value, SettingType type = SettingType::Binary) { 107 | auto it = m_settings.find(name); 108 | if (it != m_settings.end()) { 109 | it->second.Set(value); 110 | } 111 | else { 112 | Setting s(name, value, type); 113 | m_settings.insert({ name, std::move(s) }); 114 | } 115 | } 116 | 117 | void Set(PCWSTR name, int value); 118 | void Set(PCWSTR name, std::vector const& values); 119 | void SetString(PCWSTR name, PCWSTR value); 120 | bool SaveWindowPosition(HWND hWnd, PCWSTR name); 121 | bool LoadWindowPosition(HWND hWnd, PCWSTR name) const; 122 | 123 | std::wstring GetString(PCWSTR name) const; 124 | 125 | template 126 | T GetValue(PCWSTR name) const { 127 | auto it = m_settings.find(name); 128 | ATLASSERT(it != m_settings.end()); 129 | ATLASSERT(it->second.Size == sizeof(T)); 130 | return *(T*)it->second.Buffer.get(); 131 | } 132 | 133 | template 134 | T& GetValueRef(PCWSTR name) const { 135 | auto it = m_settings.find(name); 136 | ATLASSERT(it != m_settings.end()); 137 | ATLASSERT(it->second.Size == sizeof(T)); 138 | return *(T*)it->second.Buffer.get(); 139 | } 140 | 141 | template 142 | T GetValueOrDefault(PCWSTR name, const T& def = T()) const { 143 | auto it = m_settings.find(name); 144 | if (it == m_settings.end()) 145 | return def; 146 | ATLASSERT(it->second.Size >= sizeof(T)); 147 | return *(T*)it->second.Buffer.get(); 148 | } 149 | 150 | int GetInt32(PCWSTR name) const; 151 | 152 | std::vector GetMultiString(PCWSTR name) const { 153 | auto it = m_settings.find(name); 154 | if (it == m_settings.end()) 155 | return {}; 156 | 157 | auto p = it->second.Buffer.get(); 158 | std::vector values; 159 | while (*p) { 160 | values.push_back(std::wstring((PCWSTR)p)); 161 | p += (wcslen((PCWSTR)p) + 1) * sizeof(WCHAR); 162 | } 163 | return values; 164 | } 165 | 166 | template 167 | const T* GetBinary(PCWSTR name) const { 168 | auto it = m_settings.find(name); 169 | if (it == m_settings.end()) 170 | return nullptr; 171 | ATLASSERT(it->second.Size == sizeof(T)); 172 | return (T*)it->second.Buffer.get(); 173 | } 174 | 175 | protected: 176 | struct LessNoCase { 177 | bool operator()(const std::wstring& s1, const std::wstring& s2) const { 178 | return ::_wcsicmp(s1.c_str(), s2.c_str()) < 0; 179 | } 180 | }; 181 | std::map m_settings; 182 | std::wstring m_path; 183 | }; 184 | 185 | -------------------------------------------------------------------------------- /WTLHelper/HexControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Selection.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct HexControlColors { 10 | COLORREF Text{ ::GetSysColor(COLOR_WINDOWTEXT) }; 11 | COLORREF Background{ ::GetSysColor(COLOR_WINDOW) }; 12 | COLORREF Ascii{ RGB(128, 0, 0) }; 13 | COLORREF Offset{ RGB(0, 0, 128) }; 14 | COLORREF SelectionText{ ::GetSysColor(COLOR_HIGHLIGHTTEXT) }; 15 | COLORREF SelectionBackground{ ::GetSysColor(COLOR_HIGHLIGHT) }; 16 | COLORREF Modified{ RGB(255, 0, 0) }; 17 | }; 18 | 19 | enum class HexControlOptions { 20 | None = 0, 21 | DisableUndoRedo = 1, 22 | GrayOutZeros = 2, 23 | }; 24 | DEFINE_ENUM_FLAG_OPERATORS(HexControlOptions); 25 | 26 | constexpr auto NMHX_SELECTION_CHANGED = 0x2000; 27 | 28 | struct NMHexControlNotify : NMHDR { 29 | }; 30 | 31 | class CHexControl : 32 | public CBufferedPaintWindowImpl { 33 | public: 34 | DECLARE_WND_CLASS_EX(L"WTLHexControl", CS_OWNDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, NULL) 35 | 36 | int64_t GetSize() const; 37 | int64_t GetData(int64_t offset, int64_t size, uint8_t*& p); 38 | int64_t GetData(int64_t offset, int64_t size, uint8_t const*& p) const; 39 | void SetReadOnly(bool readonly); 40 | bool IsReadOnly() const; 41 | bool SetInsertMode(bool insert); 42 | bool IsInsertMode() const; 43 | bool IsDataOwner() const; 44 | 45 | void SetSize(int64_t size); 46 | bool SetDataSize(int32_t size); 47 | int32_t GetDataSize() const; 48 | bool SetBytesPerLine(int32_t bytesPerLine); 49 | int32_t GetBytesPerLine() const; 50 | bool Copy(int64_t offset = -1, int64_t size = 0) const; 51 | bool Paste(int64_t offset = -1); 52 | bool HasSelection() const; 53 | bool CanCopy() const; 54 | bool CanPaste() const; 55 | bool Cut(); 56 | bool Delete(); 57 | void ClearAll(); 58 | void SetDirty(bool dirty); 59 | bool IsDirty() const; 60 | bool CanCut() const; 61 | bool CanDelete() const; 62 | Selection const& GetSelection() const; 63 | int64_t SetBiasOffset(int64_t offset); 64 | int64_t GetBiasOffset() const; 65 | int64_t Fill(int64_t offset, uint8_t value, int64_t count); 66 | HexControlColors& GetColors(); 67 | std::wstring GetText(int64_t offset, int64_t size) const; 68 | void Refresh(); 69 | bool SetData(int64_t offset, std::span data, bool update = true); 70 | bool InitData(uint8_t* p, int64_t size, bool owner = false); 71 | bool InsertData(int64_t offset, std::span data, bool update = true); 72 | bool LoadFile(PCWSTR path); 73 | 74 | BEGIN_MSG_MAP(CHexControl) 75 | MESSAGE_HANDLER(WM_CHAR, OnChar) 76 | MESSAGE_HANDLER(WM_VSCROLL, OnVScroll) 77 | MESSAGE_HANDLER(WM_HSCROLL, OnHScroll) 78 | MESSAGE_HANDLER(WM_SIZE, OnSize) 79 | MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) 80 | MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) 81 | MESSAGE_HANDLER(WM_LBUTTONUP, OnLeftButtonUp) 82 | MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLeftButtonDown) 83 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 84 | MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel) 85 | MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) 86 | MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) 87 | MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDialogCode) 88 | MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) 89 | CHAIN_MSG_MAP(CBufferedPaintWindowImpl) 90 | END_MSG_MAP() 91 | 92 | void DoPaint(CDCHandle dc, RECT& rect); 93 | 94 | private: 95 | LRESULT OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 96 | LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 97 | LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 98 | LRESULT OnHScroll(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 99 | LRESULT OnVScroll(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 100 | LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 101 | LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 102 | LRESULT OnKillFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 103 | LRESULT OnLeftButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 104 | LRESULT OnGetDialogCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 105 | LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 106 | LRESULT OnChar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 107 | LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 108 | LRESULT OnLeftButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); 109 | 110 | private: 111 | void CancelEdit(); 112 | bool CopyText(PCWSTR text) const; 113 | void SendSelectionChanged(); 114 | void RecalcLayout(); 115 | void InitFontMetrics(); 116 | CPoint GetPointFromOffset(int64_t offset) const; 117 | int64_t GetOffsetFromPoint(const POINT& pt) const; 118 | void DrawNumber(CDCHandle dc, int64_t offset, uint64_t value, uint32_t editDigits); 119 | void DrawAscii(CDCHandle dc, int64_t offset, int chars); 120 | CString FormatNumber(ULONGLONG number, int size = 0) const; 121 | 122 | void UpdateCaret(); 123 | void RedrawWindow(RECT* = nullptr); 124 | void ClearSelection(); 125 | void CommitValue(int64_t offset, uint64_t value); 126 | void ResetInput(); 127 | int64_t NormalizeOffset(int64_t& offset) const; 128 | void RedrawCaretLine(); 129 | 130 | private: 131 | HexControlColors m_Colors; 132 | CFont m_Font; 133 | std::vector m_Data; 134 | uint8_t* m_pBuffer{ nullptr }; 135 | int64_t m_Size{ 0 }; 136 | std::wstring m_FileName; 137 | int m_FontPointSize{ 110 }; 138 | int m_Lines{ 1 }; 139 | int m_Chars{ 0 }; 140 | int m_CharWidth, m_CharHeight; 141 | std::vector m_Text; 142 | int64_t m_StartOffset{ 0 }, m_BiasOffset{ 0 }; 143 | uint32_t m_DataSize{ 1 }, m_BytesPerLine{ 32 }; 144 | int64_t m_CaretOffset{ 0 }; 145 | int m_AddressDigits{ 8 }; 146 | int m_EditDigits{ 0 }, m_LastDigits{ 0 }; 147 | Selection m_Selection; 148 | uint64_t m_CurrentInput{ 0 }, m_OldValue; 149 | std::unordered_set m_Modified; 150 | NMHexControlNotify m_Notify; 151 | bool m_InsertMode : 1 { true }; 152 | bool m_ReadOnly : 1 { false }; 153 | bool m_Dirty : 1 { false }; 154 | bool m_AllowGrow : 1 { false }; 155 | }; 156 | 157 | -------------------------------------------------------------------------------- /WTLHelper/WTLHelper.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Helpers 13 | 14 | 15 | Helpers 16 | 17 | 18 | Controls 19 | 20 | 21 | Helpers 22 | 23 | 24 | Controls 25 | 26 | 27 | Helpers 28 | 29 | 30 | Helpers 31 | 32 | 33 | Helpers 34 | 35 | 36 | Helpers 37 | 38 | 39 | Helpers 40 | 41 | 42 | Helpers 43 | 44 | 45 | Helpers 46 | 47 | 48 | CompoundFiles 49 | 50 | 51 | CompoundFiles 52 | 53 | 54 | Helpers 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Helpers 68 | 69 | 70 | Helpers 71 | 72 | 73 | Controls 74 | 75 | 76 | Controls 77 | 78 | 79 | Controls 80 | 81 | 82 | Controls 83 | 84 | 85 | Controls 86 | 87 | 88 | Controls 89 | 90 | 91 | Controls 92 | 93 | 94 | Controls 95 | 96 | 97 | Controls 98 | 99 | 100 | Controls 101 | 102 | 103 | Controls 104 | 105 | 106 | Helpers 107 | 108 | 109 | Controls 110 | 111 | 112 | Helpers 113 | 114 | 115 | Helpers 116 | 117 | 118 | Helpers 119 | 120 | 121 | Controls 122 | 123 | 124 | Helpers 125 | 126 | 127 | Helpers 128 | 129 | 130 | CompoundFiles 131 | 132 | 133 | CompoundFiles 134 | 135 | 136 | Controls 137 | 138 | 139 | Helpers 140 | 141 | 142 | Resources 143 | 144 | 145 | Controls 146 | 147 | 148 | Helpers 149 | 150 | 151 | Controls 152 | 153 | 154 | Controls 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | {80afb784-1648-4f6e-a265-79846f6ed7b2} 163 | 164 | 165 | {e0c05608-3785-46b8-8e10-f7e92ffa9fc2} 166 | 167 | 168 | {710a3743-66ec-4746-b502-aacc34dfc091} 169 | 170 | 171 | {231c6c75-96a7-4165-a773-29ca474d7abf} 172 | 173 | 174 | 175 | 176 | Resources 177 | 178 | 179 | Resources 180 | 181 | 182 | Resources 183 | 184 | 185 | Resources 186 | 187 | 188 | 189 | 190 | Resources 191 | 192 | 193 | -------------------------------------------------------------------------------- /WTLHelper/ListViewhelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ListViewHelper.h" 3 | #include "IListView.h" 4 | #include 5 | #include "VirtualListView.h" 6 | 7 | bool ListViewHelper::SaveAll(PCWSTR path, CListViewCtrl& lv, PCWSTR separator, bool includeHeaders) { 8 | wil::unique_handle hFile(::CreateFile(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)); 9 | if (!hFile) 10 | return false; 11 | 12 | auto count = lv.GetItemCount(); 13 | auto header = lv.GetHeader(); 14 | auto columns = header.GetItemCount(); 15 | CString text; 16 | DWORD written; 17 | 18 | if (includeHeaders) { 19 | HDITEM hdi; 20 | WCHAR text[64] = { 0 }; 21 | hdi.cchTextMax = _countof(text); 22 | hdi.pszText = text; 23 | hdi.mask = HDI_TEXT; 24 | for (int i = 0; i < columns; i++) { 25 | header.GetItem(i, &hdi); 26 | ::wcscat_s(text, i == columns - 1 ? L"\n" : separator); 27 | ::WriteFile(hFile.get(), text, (DWORD)::wcslen(text) * sizeof(WCHAR), &written, nullptr); 28 | } 29 | } 30 | 31 | for (int i = 0; i < count; i++) { 32 | for (int c = 0; c < columns; c++) { 33 | text.Empty(); 34 | lv.GetItemText(i, c, text); 35 | text += c == columns - 1 ? L"\n" : separator; 36 | ::WriteFile(hFile.get(), text.GetBuffer(), text.GetLength() * sizeof(WCHAR), &written, nullptr); 37 | } 38 | } 39 | 40 | return true; 41 | } 42 | 43 | CString ListViewHelper::GetRowAsString(CListViewCtrl const& lv, int row, PCWSTR separator) { 44 | auto count = lv.GetHeader().GetItemCount(); 45 | if (count == 0) 46 | return L""; 47 | 48 | CString text, item; 49 | for (int c = 0; c < count; c++) { 50 | if (lv.GetItemText(row, c, item)) 51 | item.Trim(L"\n\r"); 52 | text += item; 53 | if (c < count - 1) 54 | text += separator; 55 | } 56 | return text; 57 | } 58 | 59 | CString ListViewHelper::GetRowColumnsAsString(CListViewCtrl const& lv, int row, int start, int count, PCWSTR separator) { 60 | if(count == 0) 61 | count = lv.GetHeader().GetItemCount(); 62 | if (count == 0) 63 | return L""; 64 | 65 | CString text, item; 66 | for (int c = 0; c < count; c++) { 67 | if (lv.GetItemText(row, c + start, item)) 68 | item.Trim(L"\n\r"); 69 | text += item; 70 | if (c < count - 1) 71 | text += separator; 72 | } 73 | return text; 74 | } 75 | 76 | CString ListViewHelper::GetSelectedRowsAsString(CListViewCtrl const& lv, PCWSTR separator, PCWSTR cr) { 77 | CString text; 78 | for (auto line : SelectedItemsView(lv)) { 79 | text += GetRowAsString(lv, line, separator) += cr; 80 | } 81 | if (!text.IsEmpty()) 82 | text = text.Left(text.GetLength() - 2); 83 | return text; 84 | } 85 | 86 | int ListViewHelper::FindItem(CListViewCtrl const& lv, PCWSTR text, bool partial) { 87 | auto columns = lv.GetHeader().GetItemCount(); 88 | CString stext(text); 89 | stext.MakeLower(); 90 | for (int i = 0; i < lv.GetItemCount(); i++) { 91 | for (int c = 0; c < columns; c++) { 92 | CString text; 93 | lv.GetItemText(i, c, text); 94 | text.MakeLower(); 95 | if (partial && text.Find(stext) >= 0) 96 | return i; 97 | if (!partial && text == stext) 98 | return i; 99 | } 100 | } 101 | 102 | return -1; 103 | } 104 | 105 | int ListViewHelper::SearchItem(CListViewCtrl const& lv, PCWSTR textToFind, bool searchDown, bool caseSenstive) { 106 | int start = lv.GetNextItem(-1, LVIS_SELECTED); 107 | CString find(textToFind); 108 | auto ignoreCase = !caseSenstive; 109 | if (ignoreCase) 110 | find.MakeLower(); 111 | 112 | auto columns = lv.GetHeader().GetItemCount(); 113 | auto count = lv.GetItemCount(); 114 | int from = searchDown ? start + 1 : start - 1 + count; 115 | int to = searchDown ? count + start : start + 1; 116 | int step = searchDown ? 1 : -1; 117 | 118 | int findIndex = -1; 119 | CString text; 120 | for (int i = from; i != to; i += step) { 121 | int index = i % count; 122 | for (int c = 0; c < columns; c++) { 123 | lv.GetItemText(index, c, text); 124 | if (ignoreCase) 125 | text.MakeLower(); 126 | if (text.Find(find) >= 0) { 127 | findIndex = index; 128 | break; 129 | } 130 | } 131 | if (findIndex >= 0) 132 | return findIndex; 133 | } 134 | 135 | return -1; 136 | } 137 | 138 | int ListViewHelper::FindRow(CListViewCtrl const& lv, PCWSTR rowText, int start) { 139 | auto count = lv.GetItemCount(); 140 | for (int i = start + 1; i < count; i++) 141 | if (GetRowAsString(lv, i) == rowText) 142 | return i; 143 | 144 | return -1; 145 | } 146 | 147 | int ListViewHelper::FindRow(CListViewCtrl const& lv, int colStart, int colCount, PCWSTR rowText, int start) { 148 | if (colCount == 0) 149 | colCount = lv.GetHeader().GetItemCount(); 150 | auto count = lv.GetItemCount(); 151 | for (int i = start + 1; i < count; i++) 152 | if (GetRowColumnsAsString(lv, i, colStart, colCount) == rowText) 153 | return i; 154 | return -1; 155 | } 156 | 157 | IListView* ListViewHelper::GetIListView(HWND hListView) { 158 | IListView* p{ nullptr }; 159 | ::SendMessage(hListView, LVM_QUERYINTERFACE, reinterpret_cast(&__uuidof(IListView)), reinterpret_cast(&p)); 160 | return p; 161 | } 162 | 163 | CString ListViewHelper::GetAllRowsAsString(CListViewCtrl const& lv, PCWSTR separator, PCWSTR cr) { 164 | CString text; 165 | int count = lv.GetItemCount(); 166 | for (int i = 0; i < count; i++) { 167 | text += GetRowAsString(lv, i, separator) += cr; 168 | } 169 | if (!text.IsEmpty()) 170 | text = text.Left(text.GetLength() - 2); 171 | return text; 172 | } 173 | 174 | bool ListViewHelper::WriteColumnsState(ColumnsState const& state, IStream* stm) { 175 | auto count = state.Count; 176 | stm->Write(&count, sizeof(count), nullptr); 177 | stm->Write(&state.SortColumn, sizeof(state.SortColumn), nullptr); 178 | stm->Write(&state.SortAscending, sizeof(state.SortAscending), nullptr); 179 | stm->Write(state.Order.get(), sizeof(int) * count, nullptr); 180 | stm->Write(state.Columns.get(), sizeof(LVCOLUMN) * count, nullptr); 181 | stm->Write(state.Tags.get(), sizeof(int) * count, nullptr); 182 | if (state.Text) { 183 | for (int i = 0; i < count; i++) { 184 | auto len = (uint16_t)state.Text[i].length(); 185 | stm->Write(&len, sizeof(len), nullptr); 186 | if (len) { 187 | stm->Write(state.Text[i].c_str(), len * sizeof(WCHAR), nullptr); 188 | } 189 | } 190 | } 191 | uint32_t end = 0xffff; 192 | stm->Write(&end, sizeof(end), nullptr); 193 | return true; 194 | } 195 | 196 | bool ListViewHelper::ReadColumnsState(ColumnsState& state, IStream* stm) { 197 | int count = 0; 198 | stm->Read(&count, sizeof(count), nullptr); 199 | if (count == 0) 200 | return false; 201 | 202 | stm->Read(&state.SortColumn, sizeof(state.SortColumn), nullptr); 203 | stm->Read(&state.SortAscending, sizeof(state.SortAscending), nullptr); 204 | state.Order = std::make_unique(count); 205 | stm->Read(state.Order.get(), sizeof(int) * count, nullptr); 206 | state.Columns = std::make_unique(count); 207 | stm->Read(state.Order.get(), sizeof(LVCOLUMN) * count, nullptr); 208 | state.Tags = std::make_unique(count); 209 | stm->Read(state.Tags.get(), sizeof(int) * count, nullptr); 210 | 211 | uint16_t len = 0; 212 | stm->Read(&len, sizeof(len), nullptr); 213 | if (len != 0xffff) { 214 | state.Text = std::make_unique(count); 215 | for (int i = 0; i < count; i++) { 216 | state.Text[i].resize(len); 217 | stm->Read(state.Text[i].data(), len * sizeof(WCHAR), nullptr); 218 | stm->Read(&len, sizeof(len), nullptr); 219 | } 220 | ATLASSERT(len == 0xffff); 221 | } 222 | return true; 223 | } 224 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /WTLHelper/OwnerDrawnMenu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct COwnerDrawnMenuBase { 7 | void SetTextColor(COLORREF color); 8 | void SetBackColor(COLORREF color); 9 | void SetSelectionTextColor(COLORREF color); 10 | void SetSelectionBackColor(COLORREF color); 11 | void SetSeparatorColor(COLORREF color); 12 | void AddCommand(UINT id, HICON hIcon); 13 | void AddCommand(UINT id, UINT iconId); 14 | bool AddMenu(CMenuHandle hMenu); 15 | bool AddMenu(UINT id); 16 | void AddSubMenu(CMenuHandle menu); 17 | void SetCheckIcon(HICON hicon, HICON hRadioIcon = nullptr); 18 | void SetCheckIcon(UINT iconId, UINT radioId = 0); 19 | void UpdateMenuBase(CMenuHandle menu, bool subMenus = false); 20 | 21 | protected: 22 | int m_Width{ 0 }; 23 | struct ItemData { 24 | CString Text; 25 | int Image; 26 | }; 27 | std::unordered_map m_Items; 28 | CImageList m_Images; 29 | COLORREF m_TextColor{ RGB(0, 0, 0) }, m_BackColor{ ::GetSysColor(COLOR_WINDOW) }; 30 | COLORREF m_SelectionBackColor{ ::GetSysColor(COLOR_HIGHLIGHT) }, m_SelectionTextColor{ ::GetSysColor(COLOR_HIGHLIGHTTEXT) }; 31 | COLORREF m_SeparatorColor{ RGB(64, 64, 64) }; 32 | int m_LastHeight{ 16 }; 33 | int m_CheckIcon{ -1 }; 34 | int m_RadioIcon{ -1 }; 35 | enum { TopLevelMenu = 111, Separator = 100 }; 36 | }; 37 | 38 | template 39 | struct COwnerDrawnMenu : COwnerDrawnMenuBase { 40 | BEGIN_MSG_MAP(COwnerDrawnMenu) 41 | MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) 42 | MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem) 43 | END_MSG_MAP() 44 | 45 | void UpdateMenu(CMenuHandle menu, bool subMenus = false) { 46 | UpdateMenuBase(menu, subMenus); 47 | ::DrawMenuBar(static_cast(this)->m_hWnd); 48 | } 49 | 50 | LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { 51 | if (wParam) { 52 | static_cast(this)->SetMsgHandled(FALSE); 53 | return 0; 54 | } 55 | static_cast(this)->SetMsgHandled(TRUE); 56 | DrawItem((LPDRAWITEMSTRUCT)lParam); 57 | bHandled = static_cast(this)->IsMsgHandled(); 58 | return TRUE; 59 | } 60 | 61 | LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { 62 | if (wParam) { 63 | static_cast(this)->SetMsgHandled(FALSE); 64 | return 0; 65 | } 66 | static_cast(this)->SetMsgHandled(TRUE); 67 | MeasureItem((LPMEASUREITEMSTRUCT)lParam); 68 | bHandled = static_cast(this)->IsMsgHandled(); 69 | return TRUE; 70 | } 71 | 72 | COwnerDrawnMenu() { 73 | m_Images.Create(16, 16, ILC_COLOR32 | ILC_COLOR | ILC_MASK, 16, 8); 74 | } 75 | 76 | BOOL ShowContextMenu(HMENU hMenu, DWORD flags, int x, int y, HWND hWnd = nullptr) { 77 | AddSubMenu(hMenu); 78 | return ::TrackPopupMenu(hMenu, flags, x, y, 0, hWnd ? hWnd : static_cast(this)->m_hWnd, nullptr); 79 | } 80 | 81 | void DrawSeparator(CDCHandle dc, CRect& rc) { 82 | dc.FillSolidRect(&rc, m_BackColor); 83 | CPen pen; 84 | pen.CreatePen(PS_SOLID, 1, m_SeparatorColor); 85 | dc.MoveTo(rc.left + 8, rc.top + rc.Height() / 2); 86 | dc.SelectPen(pen); 87 | dc.LineTo(rc.right - 8, rc.top + rc.Height() / 2); 88 | } 89 | 90 | void DrawItem(LPDRAWITEMSTRUCT dis) { 91 | auto p = static_cast(this); 92 | if (dis->CtlType != ODT_MENU || !::IsMenu((HMENU)dis->hwndItem)) { 93 | p->SetMsgHandled(FALSE); 94 | return; 95 | } 96 | 97 | CDCHandle dc(dis->hDC); 98 | CRect rc(dis->rcItem); 99 | if (dis->itemData == Separator) { 100 | DrawSeparator(dc, rc); 101 | return; 102 | } 103 | 104 | bool enabled = (dis->itemState & (ODS_DISABLED | ODS_GRAYED)) == 0; 105 | 106 | dc.FillSolidRect(&rc, (dis->itemState & ODS_SELECTED) ? m_SelectionBackColor : m_BackColor); 107 | rc.OffsetRect(2, 2); 108 | rc.right = rc.left + 16; 109 | rc.bottom = rc.top + 16; 110 | 111 | auto it = m_Items.find(dis->itemID); 112 | if (it != m_Items.end()) { 113 | auto& data = it->second; 114 | if (data.Image >= 0) { 115 | m_Images.DrawEx(data.Image, dis->hDC, rc, CLR_NONE, CLR_NONE, ILD_NORMAL); 116 | if (dis->itemState & ODS_CHECKED) { 117 | rc.InflateRect(2, 2); 118 | CBrush brush; 119 | brush.CreateSolidBrush(m_TextColor); 120 | dc.FrameRect(&rc, brush); 121 | } 122 | } 123 | else if (dis->itemState & ODS_CHECKED) { 124 | // draw a checkmark 125 | bool radio = p->UIGetState(dis->itemID) & CUpdateUIBase::UPDUI_RADIO; 126 | m_Images.DrawEx((radio && m_RadioIcon >= 0) ? m_RadioIcon : m_CheckIcon, dis->hDC, rc, CLR_NONE, CLR_NONE, ILD_NORMAL); 127 | } 128 | } 129 | else if (dis->itemState & ODS_CHECKED) { 130 | // draw a checkmark 131 | bool radio = p->UIGetState(dis->itemID) & CUpdateUIBase::UPDUI_RADIO; 132 | m_Images.DrawEx((radio && m_RadioIcon >= 0) ? m_RadioIcon : m_CheckIcon, dis->hDC, rc, CLR_NONE, CLR_NONE, ILD_NORMAL); 133 | } 134 | CMenuHandle menu((HMENU)dis->hwndItem); 135 | ATLASSERT(menu.IsMenu()); 136 | MENUITEMINFO mii = { sizeof(mii) }; 137 | mii.fMask = MIIM_SUBMENU; 138 | if (dis->itemData != TopLevelMenu && menu.GetMenuItemInfo(dis->itemID, FALSE, &mii) && mii.hSubMenu) { 139 | CRect rc(dis->rcItem); 140 | rc.DeflateRect(10, 6); 141 | rc.left = rc.right - 10; 142 | if (enabled) { 143 | HBRUSH brush = ::GetSysColorBrush(COLOR_GRAYTEXT); 144 | POINT pt[] = { { rc.left, rc.top }, { rc.left, rc.bottom }, { rc.right, rc.top + rc.Height() / 2 } }; 145 | CPen pen; 146 | pen.CreatePen(PS_SOLID, 1, m_TextColor); 147 | dc.SelectPen(pen); 148 | dc.SelectBrush(brush); 149 | dc.Polygon(pt, _countof(pt)); 150 | rc.right = dis->rcItem.right; 151 | } 152 | dc.ExcludeClipRect(&rc); 153 | } 154 | 155 | WCHAR mtext[128]; 156 | auto text = (p->UIGetState(dis->itemID) & CUpdateUIBase::UPDUI_TEXT) ? p->UIGetText(dis->itemID) : nullptr; 157 | if (text == nullptr) 158 | if (menu.GetMenuString(dis->itemID, mtext, _countof(mtext), MF_BYCOMMAND)) 159 | text = mtext; 160 | 161 | if (text && text[0]) { 162 | if (it != m_Items.end()) { 163 | it->second.Text = text; 164 | } 165 | rc = dis->rcItem; 166 | if (dis->itemData != TopLevelMenu) { 167 | rc.left += 24; 168 | rc.right -= 8; 169 | } 170 | if (dis->itemState & ODS_DISABLED) 171 | dc.SetTextColor(RGB(128, 128, 128)); 172 | else 173 | dc.SetTextColor((dis->itemState & ODS_SELECTED) ? m_SelectionTextColor : m_TextColor); 174 | dc.SetBkMode(TRANSPARENT); 175 | if (dis->itemData == TopLevelMenu) { 176 | dc.DrawText(text, -1, &rc, DT_VCENTER | DT_SINGLELINE | DT_CENTER); 177 | } 178 | else { 179 | CString stext(text); 180 | auto tab = stext.Find(L'\t'); 181 | if (tab >= 0) 182 | stext.SetAt(tab, 0); 183 | dc.DrawText(stext, -1, &rc, DT_VCENTER | DT_SINGLELINE); 184 | if (tab >= 0) 185 | dc.DrawText(stext.Mid(tab + 1), -1, &rc, DT_VCENTER | DT_SINGLELINE | DT_RIGHT); 186 | } 187 | } 188 | } 189 | 190 | void MeasureItem(LPMEASUREITEMSTRUCT mis) { 191 | auto p = static_cast(this); 192 | if (mis->CtlType != ODT_MENU) { 193 | p->SetMsgHandled(FALSE); 194 | return; 195 | } 196 | 197 | mis->itemWidth = 0; 198 | mis->itemHeight = m_LastHeight; 199 | if (mis->itemData == Separator) // separator 200 | mis->itemHeight = 10; 201 | else if (mis->itemID) { 202 | auto text = (p->UIGetState(mis->itemID) & CUpdateUIBase::UPDUI_TEXT) ? p->UIGetText(mis->itemID) : nullptr; 203 | CString stext; 204 | if (text == nullptr) { 205 | if (auto it = m_Items.find(mis->itemID); it != m_Items.end()) { 206 | stext = (it->second.Text); 207 | } 208 | } 209 | else { 210 | stext = text; 211 | } 212 | CClientDC dc(static_cast(this)->m_hWnd); 213 | CSize size; 214 | stext.Remove(L'&'); 215 | if (stext.IsEmpty()) 216 | stext = L"M"; 217 | if (dc.GetTextExtent(stext, stext.GetLength(), &size)) { 218 | mis->itemWidth = size.cx + (mis->itemData == TopLevelMenu ? -5 : 25); 219 | m_LastHeight = mis->itemHeight = size.cy + (mis->itemData == TopLevelMenu ? 10 : 6); 220 | } 221 | } 222 | 223 | if (mis->itemData != TopLevelMenu) { 224 | if (mis->itemWidth < 120) 225 | mis->itemWidth = 120; 226 | } 227 | } 228 | 229 | }; 230 | -------------------------------------------------------------------------------- /WTLHelper/ThemeHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include 3 | #include 4 | #include "ThemeHelper.h" 5 | #include "Theme.h" 6 | #include "CustomEdit.h" 7 | #include "SizeGrip.h" 8 | #include "CustomStatusBar.h" 9 | #include "CustomButton.h" 10 | #include "CustomDialog.h" 11 | #include "CustomHeader.h" 12 | #include "CustomRebar.h" 13 | #include "CustomListView.h" 14 | #include "OwnerDrawnMenu.h" 15 | #include "CustomTabControl.h" 16 | #include "CustomTreeView.h" 17 | #include "CustomToolBar.h" 18 | #include "CustomComboBox.h" 19 | 20 | const Theme* CurrentTheme; 21 | Theme g_DefaultTheme{ true }; 22 | std::atomic SuspendCount; 23 | 24 | static decltype(::GetSysColor)* OrgGetSysColor = ::GetSysColor; 25 | static decltype(::GetSysColorBrush)* OrgGetSysColorBrush = ::GetSysColorBrush; 26 | static decltype(::GetSystemMetrics)* OrgGetSystemMetrics = ::GetSystemMetrics; 27 | static decltype(::SetTextColor)* OrgSetTextColor = ::SetTextColor; 28 | static decltype(::ReleaseDC)* OrgReleaseDC = ::ReleaseDC; 29 | 30 | int WINAPI HookedGetSystemMetrics(_In_ int index) { 31 | return OrgGetSystemMetrics(index); 32 | } 33 | 34 | HBRUSH WINAPI HookedGetSysColorBrush(int index) { 35 | if (CurrentTheme && SuspendCount == 0) { 36 | auto hBrush = CurrentTheme->GetSysBrush(index); 37 | if (hBrush) 38 | return hBrush; 39 | } 40 | return OrgGetSysColorBrush(index); 41 | } 42 | 43 | COLORREF WINAPI HookedGetSysColor(int index) { 44 | if (CurrentTheme && SuspendCount == 0) { 45 | auto color = CurrentTheme->GetSysColor(index); 46 | if (color != CLR_INVALID) 47 | return color; 48 | } 49 | return OrgGetSysColor(index); 50 | } 51 | 52 | void HandleCreateWindow(CWPRETSTRUCT* cs) { 53 | CString name; 54 | CWindow win(cs->hwnd); 55 | auto lpcs = (LPCREATESTRUCT)cs->lParam; 56 | if (!::GetClassName(cs->hwnd, name.GetBufferSetLength(32), 32)) 57 | return; 58 | 59 | if (name.CompareNoCase(WC_COMBOBOX) != 0) { 60 | if ((lpcs->style & (WS_THICKFRAME | WS_CAPTION | WS_POPUP | WS_DLGFRAME)) == 0) 61 | ::SetWindowTheme(cs->hwnd, L" ", L""); 62 | } 63 | if (name.CompareNoCase(WC_COMBOBOX) == 0) { 64 | auto win = new CCustomComboBox; 65 | ATLVERIFY(win->SubclassWindow(cs->hwnd)); 66 | } 67 | else if (name.CompareNoCase(L"ComboLBox") == 0) { 68 | auto win = new CCustomComboLBox; 69 | ATLVERIFY(win->SubclassWindow(cs->hwnd)); 70 | } 71 | else if (name.CompareNoCase(L"EDIT") == 0 || name.CompareNoCase(L"ATL:EDIT") == 0) { 72 | auto win = new CCustomEdit; 73 | ATLVERIFY(win->SubclassWindow(cs->hwnd)); 74 | } 75 | else if (name.CompareNoCase(WC_LISTVIEW) == 0) { 76 | auto win = new CCustomListView; 77 | win->SubclassWindow(cs->hwnd); 78 | } 79 | else if (name.CompareNoCase(WC_TREEVIEW) == 0 || name.CompareNoCase(CString(L"ATL:") + WC_TREEVIEW) == 0) { 80 | auto win = new CCustomTreeView; 81 | win->SubclassWindow(cs->hwnd); 82 | win->Init(); 83 | } 84 | else if (name.CompareNoCase(WC_TABCONTROL) == 0 || name.CompareNoCase(L"ATL:" WC_TABCONTROL) == 0) { 85 | auto win = new CCustomTabControlParent; 86 | ATLVERIFY(win->SubclassWindow(lpcs->hwndParent)); 87 | win->Init(cs->hwnd); 88 | } 89 | else if (name.CompareNoCase(REBARCLASSNAME) == 0) { 90 | //::SetWindowTheme(cs->hwnd, nullptr, nullptr); 91 | auto win = new CCustomRebar; 92 | win->SubclassWindow(cs->hwnd); 93 | } 94 | else if (name.CompareNoCase(TOOLBARCLASSNAME) == 0) { 95 | ::SetWindowTheme(cs->hwnd, nullptr, nullptr); 96 | auto win = new CCustomToolBarParent; 97 | win->Init(cs->hwnd); 98 | } 99 | else if (name.CompareNoCase(WC_HEADER) == 0 || name.CompareNoCase("ATL:" WC_HEADER) == 0) { 100 | ::SetWindowTheme(cs->hwnd, nullptr, nullptr); 101 | auto win = new CCustomHeaderParent; 102 | win->SubclassWindow(lpcs->hwndParent); 103 | win->Init(cs->hwnd); 104 | } 105 | else if (name.CompareNoCase(L"#32770") == 0) { // dialog 106 | auto win = new CCustomDialog; 107 | ATLVERIFY(win->SubclassWindow(cs->hwnd)); 108 | //win->ModifyStyle(WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0); 109 | } 110 | else if (name.CompareNoCase(STATUSCLASSNAME) == 0) { 111 | ::SetWindowTheme(cs->hwnd, nullptr, nullptr); 112 | auto pwin = new CCustomStatusBarParent; 113 | pwin->Init(cs->hwnd, ::GetParent(cs->hwnd)); 114 | } 115 | else if (name.CompareNoCase(L"ScrollBar") == 0) { 116 | if (lpcs->style & SBS_SIZEGRIP) { 117 | auto win = new CSizeGrip; 118 | ATLVERIFY(win->SubclassWindow(cs->hwnd)); 119 | } 120 | else { 121 | //auto win = new CCustomScrollBar; 122 | //win->SubclassWindow(cs->hwnd); 123 | } 124 | } 125 | else if (name.CompareNoCase(WC_BUTTON) == 0) { 126 | auto type = lpcs->style & BS_TYPEMASK; 127 | if (type == BS_PUSHBUTTON || type == BS_DEFPUSHBUTTON || type == BS_GROUPBOX) { 128 | auto win = new CCustomButtonParent; 129 | win->Init(cs->hwnd); 130 | } 131 | } 132 | } 133 | 134 | LRESULT CALLBACK CallWndProc(int action, WPARAM wp, LPARAM lp) { 135 | if (SuspendCount == 0 && action == HC_ACTION) { 136 | auto cs = reinterpret_cast(lp); 137 | 138 | switch (cs->message) { 139 | case WM_CREATE: 140 | HandleCreateWindow(cs); 141 | break; 142 | 143 | } 144 | } 145 | 146 | return ::CallNextHookEx(nullptr, action, wp, lp); 147 | } 148 | 149 | bool ThemeHelper::SetNativeDarkMode(bool dark) { 150 | static bool (WINAPI * ShouldAppsUseDarkMode)(); 151 | static bool (WINAPI * AllowDarkModeForWindow)(HWND hwnd, bool allow); 152 | static DWORD(WINAPI * SetPreferredAppMode)(DWORD); 153 | if(!SetPreferredAppMode) { 154 | auto hDll = ::LoadLibrary(L"uxtheme.dll"); 155 | if (!hDll) 156 | return false; 157 | 158 | ShouldAppsUseDarkMode = reinterpret_cast(::GetProcAddress(hDll, MAKEINTRESOURCEA(132))); 159 | AllowDarkModeForWindow = reinterpret_cast(::GetProcAddress(hDll, MAKEINTRESOURCEA(133))); 160 | SetPreferredAppMode = reinterpret_cast(::GetProcAddress(hDll, MAKEINTRESOURCEA(135))); 161 | } 162 | if (!SetPreferredAppMode) 163 | return false; 164 | 165 | SetPreferredAppMode(dark ? 2 : 1); 166 | return true; 167 | } 168 | 169 | bool ThemeHelper::Init(HANDLE hThread) { 170 | auto hook = ::SetWindowsHookEx(WH_CALLWNDPROCRET, CallWndProc, nullptr, ::GetThreadId(hThread)); 171 | if (!hook) 172 | return false; 173 | 174 | if (NOERROR != DetourTransactionBegin()) 175 | return false; 176 | 177 | DetourUpdateThread(hThread); 178 | DetourAttach((PVOID*)&OrgGetSysColor, HookedGetSysColor); 179 | DetourAttach((PVOID*)&OrgGetSysColorBrush, HookedGetSysColorBrush); 180 | DetourAttach((PVOID*)&OrgGetSystemMetrics, HookedGetSystemMetrics); 181 | auto error = DetourTransactionCommit(); 182 | ATLASSERT(error == NOERROR); 183 | if (CurrentTheme == nullptr) 184 | CurrentTheme = &g_DefaultTheme; 185 | return error == NOERROR; 186 | } 187 | 188 | int ThemeHelper::Suspend() { 189 | return ++SuspendCount; 190 | } 191 | 192 | bool ThemeHelper::IsSuspended() { 193 | return SuspendCount > 0; 194 | } 195 | 196 | int ThemeHelper::Resume() { 197 | return --SuspendCount; 198 | } 199 | 200 | const Theme* ThemeHelper::GetCurrentTheme() { 201 | return CurrentTheme; 202 | } 203 | 204 | bool ThemeHelper::IsDefault() { 205 | return GetCurrentTheme() == nullptr || GetCurrentTheme()->IsDefault(); 206 | } 207 | 208 | void ThemeHelper::SetCurrentTheme(const Theme& theme, HWND hWnd) { 209 | CurrentTheme = &theme; 210 | if (hWnd) { 211 | CWindow(hWnd).SendMessageToDescendants(::RegisterWindowMessage(L"WTLHelperUpdateTheme"), 0, reinterpret_cast(&theme)); 212 | ::RedrawWindow(hWnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW); 213 | } 214 | } 215 | 216 | void ThemeHelper::SetDefaultTheme(HWND hWnd) { 217 | SetCurrentTheme(g_DefaultTheme, hWnd); 218 | } 219 | 220 | void ThemeHelper::UpdateMenuColors(COwnerDrawnMenuBase& menu, bool dark) { 221 | // 222 | // customize menu colors 223 | // 224 | auto theme = GetCurrentTheme(); 225 | auto& mtheme = theme->Menu; 226 | menu.SetBackColor(mtheme.BackColor); 227 | menu.SetTextColor(mtheme.TextColor); 228 | menu.SetSelectionTextColor(mtheme.SelectionTextColor); 229 | menu.SetSelectionBackColor(mtheme.SelectionBackColor); 230 | menu.SetSeparatorColor(mtheme.SeparatorColor); 231 | } 232 | 233 | -------------------------------------------------------------------------------- /WTLHelper/DarkModeHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DarkModeHelper.h" 3 | #include "IATHook.h" 4 | 5 | enum IMMERSIVE_HC_CACHE_MODE { 6 | IHCM_USE_CACHED_VALUE, 7 | IHCM_REFRESH 8 | }; 9 | 10 | // 1903 18362 11 | enum PreferredAppMode { 12 | Default, 13 | AllowDark, 14 | ForceDark, 15 | ForceLight, 16 | Max 17 | }; 18 | 19 | enum WINDOWCOMPOSITIONATTRIB { 20 | WCA_UNDEFINED = 0, 21 | WCA_NCRENDERING_ENABLED = 1, 22 | WCA_NCRENDERING_POLICY = 2, 23 | WCA_TRANSITIONS_FORCEDISABLED = 3, 24 | WCA_ALLOW_NCPAINT = 4, 25 | WCA_CAPTION_BUTTON_BOUNDS = 5, 26 | WCA_NONCLIENT_RTL_LAYOUT = 6, 27 | WCA_FORCE_ICONIC_REPRESENTATION = 7, 28 | WCA_EXTENDED_FRAME_BOUNDS = 8, 29 | WCA_HAS_ICONIC_BITMAP = 9, 30 | WCA_THEME_ATTRIBUTES = 10, 31 | WCA_NCRENDERING_EXILED = 11, 32 | WCA_NCADORNMENTINFO = 12, 33 | WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, 34 | WCA_VIDEO_OVERLAY_ACTIVE = 14, 35 | WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, 36 | WCA_DISALLOW_PEEK = 16, 37 | WCA_CLOAK = 17, 38 | WCA_CLOAKED = 18, 39 | WCA_ACCENT_POLICY = 19, 40 | WCA_FREEZE_REPRESENTATION = 20, 41 | WCA_EVER_UNCLOAKED = 21, 42 | WCA_VISUAL_OWNER = 22, 43 | WCA_HOLOGRAPHIC = 23, 44 | WCA_EXCLUDED_FROM_DDA = 24, 45 | WCA_PASSIVEUPDATEMODE = 25, 46 | WCA_USEDARKMODECOLORS = 26, 47 | WCA_LAST = 27 48 | }; 49 | 50 | struct WINDOWCOMPOSITIONATTRIBDATA { 51 | WINDOWCOMPOSITIONATTRIB Attrib; 52 | PVOID pvData; 53 | SIZE_T cbData; 54 | }; 55 | 56 | using fnRtlGetNtVersionNumbers = void (WINAPI*)(LPDWORD major, LPDWORD minor, LPDWORD build); 57 | using fnSetWindowCompositionAttribute = BOOL(WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*); 58 | // 1809 17763 59 | using fnShouldAppsUseDarkMode = bool (WINAPI*)(); // ordinal 132 60 | using fnAllowDarkModeForWindow = bool (WINAPI*)(HWND hWnd, bool allow); // ordinal 133 61 | using fnAllowDarkModeForApp = bool (WINAPI*)(bool allow); // ordinal 135, in 1809 62 | using fnFlushMenuThemes = void (WINAPI*)(); // ordinal 136 63 | using fnRefreshImmersiveColorPolicyState = void (WINAPI*)(); // ordinal 104 64 | using fnIsDarkModeAllowedForWindow = bool (WINAPI*)(HWND hWnd); // ordinal 137 65 | using fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI*)(IMMERSIVE_HC_CACHE_MODE mode); // ordinal 106 66 | using fnOpenNcThemeData = HTHEME(WINAPI*)(HWND hWnd, LPCWSTR pszClassList); // ordinal 49 67 | // 1903 18362 68 | using fnShouldSystemUseDarkMode = bool (WINAPI*)(); // ordinal 138 69 | using fnSetPreferredAppMode = PreferredAppMode(WINAPI*)(PreferredAppMode appMode); // ordinal 135, in 1903 70 | using fnIsDarkModeAllowedForApp = bool (WINAPI*)(); // ordinal 139 71 | 72 | fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr; 73 | fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = nullptr; 74 | fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr; 75 | fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr; 76 | fnFlushMenuThemes _FlushMenuThemes = nullptr; 77 | fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr; 78 | fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow = nullptr; 79 | fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast = nullptr; 80 | fnOpenNcThemeData _OpenNcThemeData = nullptr; 81 | // 1903 18362 82 | fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode = nullptr; 83 | fnSetPreferredAppMode _SetPreferredAppMode = nullptr; 84 | 85 | bool g_darkModeSupported = false; 86 | bool g_darkModeEnabled = false; 87 | DWORD g_buildNumber = 0; 88 | 89 | bool AllowDarkModeForWindow(HWND hWnd, bool allow) { 90 | if (g_darkModeSupported) 91 | return _AllowDarkModeForWindow(hWnd, allow); 92 | return false; 93 | } 94 | 95 | bool IsHighContrast() { 96 | HIGHCONTRASTW highContrast = { sizeof(highContrast) }; 97 | if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE)) 98 | return highContrast.dwFlags & HCF_HIGHCONTRASTON; 99 | return false; 100 | } 101 | 102 | void RefreshTitleBarThemeColor(HWND hWnd) { 103 | BOOL dark = FALSE; 104 | if (_IsDarkModeAllowedForWindow(hWnd) && 105 | _ShouldAppsUseDarkMode() && 106 | !IsHighContrast()) { 107 | dark = TRUE; 108 | } 109 | if (g_buildNumber < 18362) 110 | SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast(static_cast(dark))); 111 | else if (_SetWindowCompositionAttribute) { 112 | WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) }; 113 | _SetWindowCompositionAttribute(hWnd, &data); 114 | } 115 | } 116 | 117 | bool IsColorSchemeChangeMessage(LPARAM lParam) { 118 | bool is = false; 119 | if (lParam && CompareStringOrdinal(reinterpret_cast(lParam), -1, L"ImmersiveColorSet", -1, TRUE) == CSTR_EQUAL) { 120 | _RefreshImmersiveColorPolicyState(); 121 | is = true; 122 | } 123 | _GetIsImmersiveColorUsingHighContrast(IHCM_REFRESH); 124 | return is; 125 | } 126 | 127 | bool IsColorSchemeChangeMessage(UINT message, LPARAM lParam) { 128 | if (message == WM_SETTINGCHANGE) 129 | return IsColorSchemeChangeMessage(lParam); 130 | return false; 131 | } 132 | 133 | void AllowDarkModeForApp(bool allow) { 134 | if (_AllowDarkModeForApp) 135 | _AllowDarkModeForApp(allow); 136 | else if (_SetPreferredAppMode) 137 | _SetPreferredAppMode(allow ? AllowDark : Default); 138 | } 139 | 140 | void FixDarkScrollBar() { 141 | HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); 142 | if (hComctl) { 143 | auto addr = FindDelayLoadThunkInModule(hComctl, "uxtheme.dll", 49); // OpenNcThemeData 144 | if (addr) { 145 | DWORD oldProtect; 146 | if (VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), PAGE_READWRITE, &oldProtect)) { 147 | auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) -> HTHEME { 148 | if (wcscmp(classList, L"ScrollBar") == 0) { 149 | hWnd = nullptr; 150 | classList = L"Explorer::ScrollBar"; 151 | } 152 | return _OpenNcThemeData(hWnd, classList); 153 | }; 154 | 155 | addr->u1.Function = reinterpret_cast(static_cast(MyOpenThemeData)); 156 | VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect); 157 | } 158 | } 159 | } 160 | } 161 | 162 | constexpr bool CheckBuildNumber(DWORD buildNumber) { 163 | return (buildNumber == 17763 || // 1809 164 | buildNumber == 18362 || // 1903 165 | buildNumber == 18363 || // 1909 166 | buildNumber >= 19041); // 2004 167 | } 168 | 169 | void InitDarkMode() { 170 | auto RtlGetNtVersionNumbers = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers")); 171 | if (RtlGetNtVersionNumbers) { 172 | DWORD major, minor; 173 | RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber); 174 | g_buildNumber &= ~0xF0000000; 175 | if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber)) { 176 | HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); 177 | if (hUxtheme) { 178 | _OpenNcThemeData = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49))); 179 | _RefreshImmersiveColorPolicyState = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104))); 180 | _GetIsImmersiveColorUsingHighContrast = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106))); 181 | _ShouldAppsUseDarkMode = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132))); 182 | _AllowDarkModeForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133))); 183 | 184 | auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135)); 185 | if (g_buildNumber < 18362) 186 | _AllowDarkModeForApp = reinterpret_cast(ord135); 187 | else 188 | _SetPreferredAppMode = reinterpret_cast(ord135); 189 | 190 | //_FlushMenuThemes = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136))); 191 | _IsDarkModeAllowedForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137))); 192 | 193 | _SetWindowCompositionAttribute = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowCompositionAttribute")); 194 | 195 | if (_OpenNcThemeData && 196 | _RefreshImmersiveColorPolicyState && 197 | _ShouldAppsUseDarkMode && 198 | _AllowDarkModeForWindow && 199 | (_AllowDarkModeForApp || _SetPreferredAppMode) && 200 | //_FlushMenuThemes && 201 | _IsDarkModeAllowedForWindow) { 202 | g_darkModeSupported = true; 203 | 204 | AllowDarkModeForApp(true); 205 | _RefreshImmersiveColorPolicyState(); 206 | 207 | g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast(); 208 | 209 | FixDarkScrollBar(); 210 | } 211 | } 212 | } 213 | } 214 | } 215 | 216 | void DarkModeHelper::Init() { 217 | InitDarkMode(); 218 | } 219 | 220 | void DarkModeHelper::AllowDarkModeForApp(bool allow) { 221 | ::AllowDarkModeForApp(allow); 222 | } 223 | 224 | void DarkModeHelper::RefreshTitleBarThemeColor(HWND hWnd) { 225 | ::RefreshTitleBarThemeColor(hWnd); 226 | } 227 | --------------------------------------------------------------------------------