├── README.MD ├── generator ├── FileItem.cpp ├── generator.rc ├── res │ ├── tick.ico │ ├── generator.ico │ └── generator.rc2 ├── base64.h ├── ClipboardHelper.h ├── encoding.h ├── DPISupport.h ├── stdafx.cpp ├── targetver.h ├── utils.h ├── Hasher.h ├── encoding.cpp ├── AdvEdit.h ├── FileItem.h ├── generator.h ├── ClipboardHelper.cpp ├── DPISupport.cpp ├── ProgressText.h ├── AdvEdit.cpp ├── resource.h ├── ListBoxEx.h ├── appDlg.h ├── Hasher.cpp ├── base64.cpp ├── stdafx.h ├── generator.cpp ├── utils.cpp ├── generator.vcxproj.filters ├── ProgressText.cpp ├── ReadMe.txt ├── appDlg.cpp ├── generator.vcxproj └── ListBoxEx.cpp ├── imgs └── sshot-stdcode.png ├── mfcDuDownloadCodeGenerator.sln ├── LICENSE ├── .gitattributes ├── .github └── workflows │ └── msvc.yml └── .gitignore /README.MD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/README.MD -------------------------------------------------------------------------------- /generator/FileItem.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/generator/FileItem.cpp -------------------------------------------------------------------------------- /generator/generator.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/generator/generator.rc -------------------------------------------------------------------------------- /generator/res/tick.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/generator/res/tick.ico -------------------------------------------------------------------------------- /imgs/sshot-stdcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/imgs/sshot-stdcode.png -------------------------------------------------------------------------------- /generator/base64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | wchar_t* base64_encode(const unsigned char* src, size_t len); 5 | -------------------------------------------------------------------------------- /generator/res/generator.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/generator/res/generator.ico -------------------------------------------------------------------------------- /generator/res/generator.rc2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jixunmoe/mfcDuDownloadCodeGenerator/HEAD/generator/res/generator.rc2 -------------------------------------------------------------------------------- /generator/ClipboardHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | 4 | void CopyStringToClipboard(CString& str, HWND hWnd = nullptr); 5 | -------------------------------------------------------------------------------- /generator/encoding.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct utf8_str { 4 | char* str; 5 | size_t size; 6 | }; 7 | 8 | void ToUTF8(utf8_str& output, wchar_t* input); 9 | -------------------------------------------------------------------------------- /generator/DPISupport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace DPISupport { 7 | uint32_t GetWindowDPI(HWND hWnd); 8 | } 9 | -------------------------------------------------------------------------------- /generator/stdafx.cpp: -------------------------------------------------------------------------------- 1 | 2 | // stdafx.cpp : source file that includes just the standard includes 3 | // generator.pch will be the pre-compiled header 4 | // stdafx.obj will contain the pre-compiled type information 5 | 6 | #include "stdafx.h" 7 | 8 | 9 | -------------------------------------------------------------------------------- /generator/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /generator/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | std::vector OpenFileDialog(CString &title, HWND hWnd); 4 | std::vector OpenDirectoryDialog(CString &title, HWND hWnd); 5 | 6 | typedef void(*f_file_callback) (const CString &strDir, const CString &strName, void* extra); 7 | void EnumFiles(const CString& srcDir, bool recursive, f_file_callback cb, void* extra = nullptr); 8 | -------------------------------------------------------------------------------- /generator/Hasher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Hasher 5 | { 6 | HCRYPTPROV m_hCryptProv = NULL; 7 | HCRYPTHASH m_phHash = NULL; 8 | HCRYPTKEY m_hKey = NULL; 9 | ALG_ID m_alg = NULL; 10 | 11 | public: 12 | bool Init(); 13 | Hasher(ALG_ID alg); 14 | ~Hasher(); 15 | 16 | void Cleanup(); 17 | 18 | CString* GetHashStr(); 19 | void Feed(char* str, int i) const; 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /generator/encoding.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "encoding.h" 3 | 4 | void ToUTF8(utf8_str& output, wchar_t* input) 5 | { 6 | size_t out_size = WideCharToMultiByte(CP_UTF8, 0, input, -1, nullptr, 0, NULL, NULL); 7 | output.str = (char*)calloc(out_size + 1, sizeof(char)); 8 | output.size = out_size; 9 | WideCharToMultiByte(CP_UTF8, 0, input, -1, output.str, static_cast(out_size), NULL, NULL); 10 | } 11 | -------------------------------------------------------------------------------- /generator/AdvEdit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | 5 | // CAdvEdit 6 | 7 | class CAdvEdit : public CEdit 8 | { 9 | DECLARE_DYNAMIC(CAdvEdit) 10 | std::mutex mutex; 11 | 12 | public: 13 | CAdvEdit(); 14 | virtual ~CAdvEdit(); 15 | void Append(const CString &text); 16 | void SelectAll(); 17 | 18 | protected: 19 | DECLARE_MESSAGE_MAP() 20 | public: 21 | afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /generator/FileItem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class CFileItem 3 | { 4 | public: 5 | CString* m_pDirectory = nullptr; 6 | CString* m_pFilename = nullptr; 7 | HICON m_hIcon = nullptr; 8 | uint64_t m_nSize = 0; 9 | 10 | CString* m_pFirstHash = nullptr; 11 | CString* m_pFullHash = nullptr; 12 | 13 | public: 14 | CFileItem(); 15 | ~CFileItem(); 16 | 17 | bool Done() const; 18 | CString GetSizeString() const; 19 | 20 | CString BDLink() const; 21 | CString DownloadCode() const; 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /generator/generator.h: -------------------------------------------------------------------------------- 1 | 2 | // generator.h : main header file for the PROJECT_NAME application 3 | // 4 | 5 | #pragma once 6 | 7 | #ifndef __AFXWIN_H__ 8 | #error "include 'stdafx.h' before including this file for PCH" 9 | #endif 10 | 11 | #include "resource.h" // main symbols 12 | 13 | 14 | // CApp: 15 | // See generator.cpp for the implementation of this class 16 | // 17 | 18 | class CApp : public CWinApp 19 | { 20 | public: 21 | CApp(); 22 | 23 | // Overrides 24 | public: 25 | virtual BOOL InitInstance(); 26 | 27 | // Implementation 28 | 29 | DECLARE_MESSAGE_MAP() 30 | }; 31 | 32 | extern CApp theApp; 33 | -------------------------------------------------------------------------------- /generator/ClipboardHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ClipboardHelper.h" 3 | 4 | void CopyStringToClipboard(CString& str, HWND hWnd) { 5 | auto pString = static_cast(str); 6 | auto dwStrByteLen = (str.GetLength() + 1) * sizeof TCHAR; 7 | 8 | HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, dwStrByteLen); 9 | if (!hMem) return; 10 | 11 | auto pLockedMem = GlobalLock(hMem); 12 | if (!pLockedMem) { 13 | GlobalFree(hMem); 14 | return; 15 | } 16 | memcpy(pLockedMem, pString, dwStrByteLen); 17 | GlobalUnlock(hMem); 18 | 19 | if (OpenClipboard(hWnd)) { 20 | EmptyClipboard(); 21 | #ifndef _UNICODE 22 | SetClipboardData(CF_TEXT, hMem); 23 | #else 24 | SetClipboardData(CF_UNICODETEXT, hMem); 25 | #endif 26 | CloseClipboard(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /generator/DPISupport.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "DPISupport.h" 4 | 5 | #if !__BUILD_FOR_XP 6 | #include 7 | #include 8 | 9 | #pragma comment(lib, "Shcore") 10 | #endif 11 | 12 | uint32_t DPISupport::GetWindowDPI(HWND hWnd) { 13 | UINT xdpi = 0; 14 | UINT ydpi = 0; 15 | 16 | #if !__BUILD_FOR_XP 17 | // Windows 8.1 or higher 18 | if (IsWindows8Point1OrGreater()) { 19 | HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); 20 | LRESULT success = 21 | GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); 22 | return success == S_OK ? ydpi : 96; 23 | } 24 | #endif 25 | 26 | HDC hDC = GetDC(hWnd); 27 | ydpi = static_cast(GetDeviceCaps(hDC, LOGPIXELSY)); 28 | ReleaseDC(hWnd, hDC); 29 | return ydpi; 30 | } 31 | -------------------------------------------------------------------------------- /generator/ProgressText.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // CProgressText 4 | 5 | class CProgressText : public CProgressCtrl 6 | { 7 | CFont* font; 8 | COLORREF progressForegroundColour = RGB(62, 231, 152); 9 | COLORREF progressBackgroundColour = RGB(0xE6, 0xE6, 0xE6); 10 | COLORREF componentBorderColour = RGB(0xBC, 0xBC, 0xBC); 11 | 12 | CBrush* progressForegroundBrush; 13 | CBrush* progressBackgroundBrush; 14 | CBrush* componentBorderBrush; 15 | void Init(); 16 | 17 | DECLARE_DYNAMIC(CProgressText) 18 | uint current = 0; 19 | uint max = 0; 20 | CString* text; 21 | double percent = 0; 22 | 23 | public: 24 | CProgressText(); 25 | virtual ~CProgressText(); 26 | void SetCurrent(uint current); 27 | void SetMax(uint max); 28 | void Increase(); 29 | void Reset(); 30 | 31 | protected: 32 | DECLARE_MESSAGE_MAP() 33 | void UpdateText(); 34 | 35 | public: 36 | afx_msg void OnPaint(); 37 | afx_msg BOOL OnEraseBkgnd(CDC* pDC); 38 | }; 39 | 40 | 41 | -------------------------------------------------------------------------------- /generator/AdvEdit.cpp: -------------------------------------------------------------------------------- 1 | // AdvEdit.cpp : implementation file 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "AdvEdit.h" 6 | 7 | // CAdvEdit 8 | 9 | IMPLEMENT_DYNAMIC(CAdvEdit, CEdit) 10 | 11 | CAdvEdit::CAdvEdit() 12 | { 13 | } 14 | 15 | CAdvEdit::~CAdvEdit() 16 | { 17 | } 18 | 19 | void CAdvEdit::Append(const CString& text) 20 | { 21 | std::lock_guard guard(mutex); 22 | 23 | auto nLength = GetWindowTextLength(); 24 | SetSel(nLength, nLength); 25 | ReplaceSel(text); 26 | } 27 | 28 | void CAdvEdit::SelectAll() 29 | { 30 | SetSel(0, GetWindowTextLength(), FALSE); 31 | } 32 | 33 | BEGIN_MESSAGE_MAP(CAdvEdit, CEdit) 34 | ON_WM_KEYDOWN() 35 | END_MESSAGE_MAP() 36 | 37 | void CAdvEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 38 | { 39 | CEdit::OnKeyDown(nChar, nRepCnt, nFlags); 40 | 41 | if (nChar == 'A') { 42 | BYTE keyState[256] = { 0 }; 43 | GetKeyboardState(keyState); 44 | bool useControl = (keyState[VK_CONTROL] & 0x80) != 0; 45 | bool useShift = (keyState[VK_SHIFT] & 0x80) != 0; 46 | bool useAlt = (keyState[VK_MENU] & 0x80) != 0; 47 | 48 | if (useControl && !useShift && !useAlt) { 49 | this->SelectAll(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /mfcDuDownloadCodeGenerator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32922.545 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ducode", "generator\generator.vcxproj", "{52BE8E83-C47E-469F-92B7-F078C51C0EBD}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Debug|x64.ActiveCfg = Debug|x64 17 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Debug|x64.Build.0 = Debug|x64 18 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Debug|x86.ActiveCfg = Debug|Win32 19 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Debug|x86.Build.0 = Debug|Win32 20 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Release|x64.ActiveCfg = Release|x64 21 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Release|x64.Build.0 = Release|x64 22 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Release|x86.ActiveCfg = Release|Win32 23 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {BC2322A7-E814-48A2-93F3-2D9DF1A6A0A4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Jixun Wu 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /generator/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by generator.rc 4 | // 5 | #define IDM_ABOUTBOX 0x0010 6 | #define IDD_ABOUTBOX 100 7 | #define IDS_ABOUTBOX 101 8 | #define IDD_GENERATOR_DIALOG 102 9 | #define IDS_PICK_FILE 102 10 | #define IDS_PICK_DIR 103 11 | #define IDS_DIR 104 12 | #define IDS_FILE_SIZE 105 13 | #define IDS_FILE_INFO 106 14 | #define IDS_LIST_PLACEHOLDER 107 15 | #define IDS_ERROR_ENCODE_FAIL 108 16 | #define IDR_MAINFRAME 128 17 | #define IDI_ICON_TICK 131 18 | #define IDC_LIST_FILES 1000 19 | #define IDC_EDIT_OUTPUT 1001 20 | #define IDC_PROG_FILE 1002 21 | #define IDC_PROG_ALL 1003 22 | #define IDC_BTN_ADD_DIR 1004 23 | #define IDC_BTN_ADD_FILE 1005 24 | #define IDC_SYSLINK_BLOG 1006 25 | #define IDC_LB_STATUS 1007 26 | #define IDC_BTN_CLEAR 1008 27 | #define IDC_CHK_RECURSIVE 1009 28 | #define IDC_BTN_COPY 1010 29 | #define IDC_GENERATE 1011 30 | #define IDC_BTN_GENERATE 1011 31 | #define IDC_CHK_URL 1012 32 | 33 | // Next default values for new objects 34 | // 35 | #ifdef APSTUDIO_INVOKED 36 | #ifndef APSTUDIO_READONLY_SYMBOLS 37 | #define _APS_NEXT_RESOURCE_VALUE 132 38 | #define _APS_NEXT_COMMAND_VALUE 32771 39 | #define _APS_NEXT_CONTROL_VALUE 1013 40 | #define _APS_NEXT_SYMED_VALUE 101 41 | #endif 42 | #endif 43 | -------------------------------------------------------------------------------- /generator/ListBoxEx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "FileItem.h" 5 | 6 | // CListBoxEx 7 | 8 | enum class ProcType 9 | { 10 | FILE_PROG, 11 | INC_FILE, 12 | ERR_FILE 13 | }; 14 | 15 | typedef void(*f_proc_file_callback) (ProcType type, double progress, CFileItem* pItem, void* extra); 16 | class CListBoxEx : public CListBox 17 | { 18 | DECLARE_DYNAMIC(CListBoxEx) 19 | private: 20 | std::mutex mutex; 21 | static HICON m_hIconTick; 22 | static CString m_sPlaceholder; 23 | static HFONT m_systemFont; 24 | bool m_bStop = false; 25 | 26 | public: 27 | CListBoxEx(); 28 | virtual ~CListBoxEx(); 29 | 30 | COLORREF m_bgSelected = RGB(204, 232, 255); // #cce8ff 31 | COLORREF m_bgClear = RGB(255, 255, 255); 32 | COLORREF m_text = RGB(0, 0, 0); 33 | COLORREF m_textSelected = RGB(0, 0, 0); 34 | COLORREF m_descText = RGB(109, 109, 109); 35 | COLORREF m_descTextSelected = RGB(109, 109, 109); 36 | 37 | protected: 38 | DECLARE_MESSAGE_MAP() 39 | 40 | public: 41 | void GetVisibleRange(int& s, int& e); 42 | bool ItemIsVisible(int nIndex); 43 | bool RedrawIfVisible(int i); 44 | void ProcessFiles(f_proc_file_callback cb, void* extra); 45 | void StopProcessing(); 46 | 47 | afx_msg void OnPaint(); 48 | virtual void MeasureItem(LPMEASUREITEMSTRUCT /*lpMeasureItemStruct*/); 49 | virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/); 50 | virtual void PreSubclassWindow(); 51 | 52 | void DrawItemData(LPDRAWITEMSTRUCT lpDrawItemStruct, CFileItem* pItem); 53 | 54 | int AddItem(const CString& srcDir, const CString& filename); 55 | void CopySelectedHashes(); 56 | int VKeyToItem(UINT /*nKey*/, UINT /*nIndex*/); 57 | virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const; 58 | BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); 59 | }; 60 | 61 | 62 | -------------------------------------------------------------------------------- /generator/appDlg.h: -------------------------------------------------------------------------------- 1 | 2 | // appDlg.h : header file 3 | // 4 | 5 | #pragma once 6 | 7 | #include "ProgressText.h" 8 | #include "AdvEdit.h" 9 | #include "ListBoxEx.h" 10 | #include "afxwin.h" 11 | #include "afxcmn.h" 12 | 13 | // CAppDlg dialog 14 | class CAppDlg : public CDialogEx 15 | { 16 | CString m_ofn_file_title; 17 | CString m_ofn_dir_title; 18 | 19 | std::mutex mutex; 20 | 21 | // Construction 22 | public: 23 | CAppDlg(CWnd* pParent = NULL); // standard constructor 24 | void AddFile(const CString& srcDir, const CString& filename); 25 | void ProcessFiles(); 26 | void AddHashEntry(CFileItem* lp_item); 27 | void ProcFile(ProcType proc, double progress, CFileItem* lp_item); 28 | void RealExit(); 29 | void AppendVersionNumber(); 30 | std::vector files; 31 | 32 | // Dialog Data 33 | #ifdef AFX_DESIGN_TIME 34 | enum { IDD = IDD_GENERATOR_DIALOG }; 35 | #endif 36 | 37 | protected: 38 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 39 | 40 | 41 | // Implementation 42 | protected: 43 | HICON m_hIcon; 44 | 45 | // Generated message map functions 46 | virtual BOOL OnInitDialog(); 47 | afx_msg void OnSysCommand(UINT nID, LPARAM lParam); 48 | afx_msg void OnPaint(); 49 | afx_msg HCURSOR OnQueryDragIcon(); 50 | DECLARE_MESSAGE_MAP() 51 | public: 52 | afx_msg void OnBnClickedGenerate(); 53 | // Current file Progress 54 | CProgressCtrl m_progFile; 55 | CProgressText m_progAll; 56 | CListBoxEx m_listFiles; 57 | CAdvEdit m_editOutput; 58 | afx_msg void OnClickedBtnAddDir(); 59 | afx_msg void OnClickedBtnAddFile(); 60 | virtual void OnCancel(); 61 | afx_msg void OnBnClickedBtnClear(); 62 | afx_msg void OnDropFiles(HDROP hDropInfo); 63 | CButton m_chkRecursive; 64 | CButton m_chkUrl; 65 | CLinkCtrl m_linkBlog; 66 | afx_msg void OnClickSyslinkBlog(NMHDR *pNMHDR, LRESULT *pResult); 67 | afx_msg void OnBnClickedBtnCopy(); 68 | CButton m_btnAddDir; 69 | CButton m_btnAddFile; 70 | CButton m_btnClear; 71 | CButton m_btnCopy; 72 | CButton m_btnGenerate; 73 | }; 74 | -------------------------------------------------------------------------------- /generator/Hasher.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Hasher.h" 3 | 4 | 5 | bool Hasher::Init() 6 | { 7 | auto bSuccess = false; 8 | 9 | if (CryptAcquireContext(&m_hCryptProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 10 | { 11 | bSuccess = CryptCreateHash(m_hCryptProv, this->m_alg, NULL, NULL, &m_phHash); 12 | 13 | if (bSuccess) return bSuccess; 14 | } 15 | Cleanup(); 16 | 17 | if (CryptAcquireContext(&m_hCryptProv, nullptr, nullptr, PROV_RSA_FULL, NULL)) 18 | { 19 | bSuccess = CryptCreateHash(m_hCryptProv, this->m_alg, NULL, NULL, &m_phHash); 20 | 21 | if (bSuccess) return bSuccess; 22 | } 23 | Cleanup(); 24 | 25 | if (CryptAcquireContext(&m_hCryptProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET)) 26 | { 27 | bSuccess = CryptCreateHash(m_hCryptProv, this->m_alg, NULL, NULL, &m_phHash); 28 | 29 | if (bSuccess) return bSuccess; 30 | } 31 | Cleanup(); 32 | 33 | return bSuccess; 34 | } 35 | 36 | Hasher::Hasher(ALG_ID alg) 37 | { 38 | this->m_alg = alg; 39 | } 40 | 41 | 42 | Hasher::~Hasher() 43 | { 44 | } 45 | 46 | void Hasher::Cleanup() 47 | { 48 | if (m_hKey) 49 | { 50 | CryptDestroyKey(m_hKey); 51 | m_hKey = NULL; 52 | } 53 | 54 | if (m_phHash) 55 | { 56 | CryptDestroyHash(m_phHash); 57 | m_phHash = NULL; 58 | } 59 | 60 | if (m_hCryptProv) 61 | { 62 | CryptReleaseContext(m_hCryptProv, NULL); 63 | m_hCryptProv = NULL; 64 | } 65 | } 66 | 67 | 68 | CString* Hasher::GetHashStr() 69 | { 70 | DWORD dwDataLen; 71 | auto r = new CString(""); 72 | if (CryptGetHashParam(this->m_phHash, HP_HASHVAL, nullptr, &dwDataLen, 0)) 73 | { 74 | BYTE* d = new BYTE[dwDataLen]; 75 | CryptGetHashParam(this->m_phHash, HP_HASHVAL, d, &dwDataLen, 0); 76 | for(DWORD i = 0; i < dwDataLen; i++) 77 | { 78 | r->AppendFormat(_T("%X%X"), d[i] >> 4, d[i] & 0x0F); 79 | } 80 | delete[] d; 81 | } 82 | Cleanup(); 83 | 84 | return r; 85 | } 86 | 87 | void Hasher::Feed(char* str, int n) const 88 | { 89 | CryptHashData(m_phHash, LPBYTE(str), n, NULL); 90 | } 91 | -------------------------------------------------------------------------------- /generator/base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Base64 encoding/decoding (RFC1341) 3 | * Copyright (c) 2005-2011, Jouni Malinen 4 | * 5 | * This software may be distributed under the terms of the BSD license. 6 | * See README for more details. 7 | */ 8 | 9 | // Change log: 10 | // 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string 11 | // instead of a buffer allocated with malloc. 12 | // 2020-12-08 - Jixun: Taken from StackOverflow and adapted to project. 13 | // source: https://stackoverflow.com/a/41094722 14 | 15 | #include "stdafx.h" 16 | #include "base64.h" 17 | 18 | static const wchar_t base64_table[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 19 | 20 | /** 21 | * base64_encode - Base64 encode 22 | * @src: Data to be encoded 23 | * @len: Length of the data to be encoded 24 | * @out_len: Pointer to output length variable, or %NULL if not used 25 | * Returns: Allocated buffer of out_len bytes of encoded data, 26 | * or empty string on failure 27 | */ 28 | wchar_t* base64_encode(const unsigned char* src, size_t len) 29 | { 30 | const unsigned char* end, * in; 31 | 32 | size_t olen; 33 | 34 | olen = 4 * ((len + 2) / 3); /* 3-byte blocks to 4-byte */ 35 | 36 | if (olen < len) 37 | return nullptr; 38 | 39 | wchar_t* out = (wchar_t*)calloc(olen + 1, sizeof(wchar_t)); 40 | if (out == nullptr) { 41 | return nullptr; 42 | } 43 | 44 | end = src + len; 45 | in = src; 46 | auto pos = out; 47 | while (end - in >= 3) { 48 | *pos++ = base64_table[in[0] >> 2]; 49 | *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; 50 | *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; 51 | *pos++ = base64_table[in[2] & 0x3f]; 52 | in += 3; 53 | } 54 | 55 | if (end - in) { 56 | *pos++ = base64_table[in[0] >> 2]; 57 | if (end - in == 1) { 58 | *pos++ = base64_table[(in[0] & 0x03) << 4]; 59 | *pos++ = L'='; 60 | } 61 | else { 62 | *pos++ = base64_table[((in[0] & 0x03) << 4) | 63 | (in[1] >> 4)]; 64 | *pos++ = base64_table[(in[1] & 0x0f) << 2]; 65 | } 66 | *pos++ = L'='; 67 | } 68 | 69 | return out; 70 | } 71 | -------------------------------------------------------------------------------- /generator/stdafx.h: -------------------------------------------------------------------------------- 1 | 2 | // stdafx.h : include file for standard system include files, 3 | // or project specific include files that are used frequently, 4 | // but are changed infrequently 5 | 6 | #pragma once 7 | 8 | #ifndef VC_EXTRALEAN 9 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 10 | #endif 11 | 12 | #include "targetver.h" 13 | 14 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit 15 | 16 | // turns off MFC's hiding of some common and often safely ignored warning messages 17 | #define _AFX_ALL_WARNINGS 18 | 19 | #include // MFC core and standard components 20 | #include // MFC extensions 21 | 22 | 23 | 24 | 25 | 26 | #ifndef _AFX_NO_OLE_SUPPORT 27 | #include // MFC support for Internet Explorer 4 Common Controls 28 | #endif 29 | #ifndef _AFX_NO_AFXCMN_SUPPORT 30 | #include // MFC support for Windows Common Controls 31 | #endif // _AFX_NO_AFXCMN_SUPPORT 32 | 33 | #include // MFC support for ribbons and control bars 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | #ifdef _UNICODE 44 | #if defined _M_IX86 45 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 46 | #elif defined _M_X64 47 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 48 | #else 49 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 50 | #endif 51 | #endif 52 | 53 | 54 | #ifndef uint 55 | #define uint unsigned int 56 | #endif 57 | 58 | #include 59 | 60 | #if _MSC_VER < 1930 61 | // less than vs2022, use the full Windows.h header 62 | #include 63 | #else 64 | // Other: use a subset of the headers. 65 | #include 66 | #include 67 | #endif 68 | #include "afxcmn.h" 69 | #include "afxwin.h" 70 | 71 | 72 | #define SAFE_DELETE(a) \ 73 | if( (a) != nullptr ) { \ 74 | delete (a); \ 75 | } \ 76 | (a) = nullptr; 77 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /generator/generator.cpp: -------------------------------------------------------------------------------- 1 | 2 | // generator.cpp : Defines the class behaviors for the application. 3 | // 4 | 5 | #include "stdafx.h" 6 | #include "generator.h" 7 | #include "appDlg.h" 8 | 9 | #ifdef _DEBUG 10 | #define new DEBUG_NEW 11 | #endif 12 | 13 | 14 | // CApp 15 | 16 | BEGIN_MESSAGE_MAP(CApp, CWinApp) 17 | ON_COMMAND(ID_HELP, &CWinApp::OnHelp) 18 | END_MESSAGE_MAP() 19 | 20 | 21 | // CApp construction 22 | 23 | CApp::CApp() 24 | { 25 | // support Restart Manager 26 | m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; 27 | 28 | // TODO: add construction code here, 29 | // Place all significant initialization in InitInstance 30 | } 31 | 32 | 33 | // The one and only CApp object 34 | 35 | CApp theApp; 36 | 37 | 38 | // CApp initialization 39 | 40 | BOOL CApp::InitInstance() 41 | { 42 | // InitCommonControlsEx() is required on Windows XP if an application 43 | // manifest specifies use of ComCtl32.dll version 6 or later to enable 44 | // visual styles. Otherwise, any window creation will fail. 45 | INITCOMMONCONTROLSEX InitCtrls; 46 | InitCtrls.dwSize = sizeof(InitCtrls); 47 | // Set this to include all the common control classes you want to use 48 | // in your application. 49 | InitCtrls.dwICC = ICC_WIN95_CLASSES; 50 | InitCommonControlsEx(&InitCtrls); 51 | 52 | CWinApp::InitInstance(); 53 | 54 | 55 | // Create the shell manager, in case the dialog contains 56 | // any shell tree view or shell list view controls. 57 | CShellManager *pShellManager = new CShellManager; 58 | 59 | // Activate "Windows Native" visual manager for enabling themes in MFC controls 60 | CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); 61 | 62 | // Standard initialization 63 | // If you are not using these features and wish to reduce the size 64 | // of your final executable, you should remove from the following 65 | // the specific initialization routines you do not need 66 | // Change the registry key under which our settings are stored 67 | // TODO: You should modify this string to be something appropriate 68 | // such as the name of your company or organization 69 | SetRegistryKey(_T("Local AppWizard-Generated Applications")); 70 | 71 | CAppDlg dlg; 72 | m_pMainWnd = &dlg; 73 | INT_PTR nResponse = dlg.DoModal(); 74 | if (nResponse == IDOK) 75 | { 76 | // TODO: Place code here to handle when the dialog is 77 | // dismissed with OK 78 | } 79 | else if (nResponse == IDCANCEL) 80 | { 81 | // TODO: Place code here to handle when the dialog is 82 | // dismissed with Cancel 83 | } 84 | else if (nResponse == -1) 85 | { 86 | TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n"); 87 | TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n"); 88 | } 89 | 90 | // Delete the shell manager created above. 91 | if (pShellManager != NULL) 92 | { 93 | delete pShellManager; 94 | } 95 | 96 | #if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS) 97 | ControlBarCleanUp(); 98 | #endif 99 | 100 | // Since the dialog has been closed, return FALSE so that we exit the 101 | // application, rather than start the application's message pump. 102 | return FALSE; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /generator/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "utils.h" 3 | 4 | std::vector OldStyleFileDialog(const CString& title, HWND hwnd) 5 | { 6 | std::vector v; 7 | CString str; 8 | OPENFILENAME ofn = {0}; 9 | ofn.lStructSize = sizeof ofn; 10 | ofn.lpstrTitle = title; 11 | ofn.hwndOwner = hwnd; 12 | ofn.lpstrFile = str.GetBuffer(1024); 13 | ofn.nMaxFile = 1024; 14 | if (GetOpenFileName(&ofn)) 15 | { 16 | v.push_back(new CString(str)); 17 | } 18 | str.ReleaseBuffer(); 19 | 20 | return v; 21 | } 22 | 23 | inline std::vector OldStyleDirectoryDialog(CString &title, HWND hWnd) 24 | { 25 | std::vector v; 26 | BROWSEINFO bi; 27 | ZeroMemory(&bi, sizeof bi); 28 | bi.hwndOwner = hWnd; 29 | // bi.lpfn = cb_set_initial; 30 | bi.lpszTitle = title; 31 | bi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE; 32 | 33 | TCHAR szPath[1024]; 34 | if (SHGetPathFromIDList(SHBrowseForFolder(&bi), szPath)) 35 | { 36 | v.push_back(new CString(szPath)); 37 | } 38 | return v; 39 | } 40 | 41 | DWORD OpenFileDialog(CString &title, HWND hWnd, DWORD flag, std::vector &v) 42 | { 43 | IFileOpenDialog* pFileOpen; 44 | do 45 | { 46 | if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, 47 | nullptr, 48 | CLSCTX_INPROC_SERVER, 49 | IID_PPV_ARGS(&pFileOpen)))) 50 | { 51 | return 1; 52 | } 53 | 54 | FILEOPENDIALOGOPTIONS fos; 55 | if (FAILED(pFileOpen->GetOptions(&fos))) break; 56 | fos |= flag | FOS_ALLOWMULTISELECT | FOS_FORCEFILESYSTEM; 57 | if (FAILED(pFileOpen->SetOptions(fos))) break; 58 | if (FAILED(pFileOpen->SetTitle(title))) break; 59 | if (FAILED(pFileOpen->Show(hWnd))) break; 60 | 61 | IShellItem* pShellItem; 62 | pFileOpen->GetResult(&pShellItem); 63 | 64 | IShellItemArray* pResults; 65 | if (FAILED(pFileOpen->GetResults(&pResults))) break; 66 | DWORD dwCount; 67 | if (FAILED(pResults->GetCount(&dwCount))) break; 68 | 69 | 70 | v.clear(); 71 | for (uint i = 0; i < dwCount; i++) 72 | { 73 | IShellItem* pItem; 74 | pResults->GetItemAt(i, &pItem); 75 | 76 | TCHAR* filePath; 77 | pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &filePath); 78 | 79 | v.push_back(new CString(filePath)); 80 | 81 | CoTaskMemFree(filePath); 82 | pItem->Release(); 83 | } 84 | 85 | if (pFileOpen) pFileOpen->Release(); 86 | return 0; 87 | } while (false); 88 | 89 | if (pFileOpen) pFileOpen->Release(); 90 | return 0; 91 | } 92 | 93 | std::vector OpenDirectoryDialog(CString &title, HWND hWnd) 94 | { 95 | std::vector r; 96 | auto err = OpenFileDialog(title, hWnd, FOS_PICKFOLDERS, r); 97 | if (err == 1) return OldStyleDirectoryDialog(title, hWnd); 98 | return r; 99 | } 100 | 101 | std::vector OpenFileDialog(CString &title, HWND hWnd) 102 | { 103 | std::vector r; 104 | auto err = OpenFileDialog(title, hWnd, 0, r); 105 | if (err == 1) return OldStyleFileDialog(title, hWnd); 106 | return r; 107 | } 108 | 109 | 110 | void EnumFiles(const CString& srcDir, bool recursive, f_file_callback cb, void* extra) { 111 | auto dir(srcDir); 112 | if (dir.Right(1).Compare(_T("\\")) == 0) 113 | { 114 | dir.AppendChar(_T('*')); 115 | } 116 | else 117 | { 118 | dir.Append(_T("\\*")); 119 | } 120 | 121 | WIN32_FIND_DATA ffd; 122 | auto hFind = FindFirstFile(dir, &ffd); 123 | if (hFind == INVALID_HANDLE_VALUE) 124 | { 125 | return; 126 | } 127 | 128 | do 129 | { 130 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 131 | { 132 | if (recursive) 133 | { 134 | if (_tcscmp(ffd.cFileName, _T(".")) != 0 135 | && _tcscmp(ffd.cFileName, _T("..")) != 0) 136 | { 137 | #if _DEBUG 138 | CString str; 139 | str.Format(_T("enter dir: %s\n"), ffd.cFileName); 140 | OutputDebugString(str); 141 | #endif 142 | EnumFiles(srcDir + _T("\\") + ffd.cFileName, recursive, cb, extra); 143 | } 144 | } 145 | } 146 | else 147 | { 148 | #if _DEBUG 149 | CString str; 150 | str.Format(_T("add file: %s\n"), ffd.cFileName); 151 | OutputDebugString(str); 152 | #endif 153 | cb(srcDir, ffd.cFileName, extra); 154 | } 155 | } while (FindNextFile(hFind, &ffd) != 0); 156 | FindClose(hFind); 157 | } 158 | -------------------------------------------------------------------------------- /.github/workflows/msvc.yml: -------------------------------------------------------------------------------- 1 | name: MSBuild 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ "main" ] 7 | 8 | env: 9 | # Path to the solution file relative to the root of the project. 10 | SOLUTION_FILE_PATH: mfcDuDownloadCodeGenerator.sln 11 | BUILD_CONFIGURATION: Release 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | matrix: 20 | build_config: 21 | - vs2019.x86_64 22 | - vs2019.static.xp.x86 23 | - vs2022.x86_64 24 | - vs2022.static.x86_64 25 | 26 | include: 27 | - build_config: vs2019.x86_64 28 | os: windows-2019 29 | - build_config: vs2019.static.xp.x86 30 | os: windows-2019 31 | - build_config: vs2022.x86_64 32 | os: windows-2022 33 | - build_config: vs2022.static.x86_64 34 | os: windows-2022 35 | 36 | runs-on: "${{ matrix.os }}" 37 | 38 | permissions: 39 | contents: write 40 | 41 | steps: 42 | - name: 🛎️ checkout 43 | uses: actions/checkout@v3 44 | 45 | - name: 🏭 prepare MSBuild 46 | uses: microsoft/setup-msbuild@v1.0.2 47 | 48 | - name: 🏭 prepare msys2 49 | uses: msys2/setup-msys2@v2 50 | with: 51 | update: true 52 | install: >- 53 | git 54 | upx 55 | zip 56 | 57 | - name: 🔧 build 58 | working-directory: ${{env.GITHUB_WORKSPACE}} 59 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 60 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 61 | run: | 62 | Set-PSDebug -Trace 1 63 | 64 | $config = "${{ matrix.build_config }}" 65 | if ($config.Contains(".static.")) { 66 | $env:_CL_ += ' /MT' 67 | $mfc_type = 'Static' 68 | } else { 69 | $env:_CL_ += ' /MD' 70 | $mfc_type = 'Dynamic' 71 | } 72 | 73 | $platform = if ($config.EndsWith('x86_64')) { "x64" } else { "x86" } 74 | $toolset = if ($config.StartsWith('vs2022.')) { "v143" } 75 | elseif ($config.Contains('.xp.')) { "v141_xp" } 76 | else { "v142" } 77 | 78 | if ($config.Contains('.xp.')) { 79 | $env:_CL_ += ' /D__BUILD_FOR_XP=1' 80 | } 81 | 82 | msbuild /m ` 83 | /p:Configuration=${{ env.BUILD_CONFIGURATION }} ` 84 | /p:Platform=$platform ` 85 | /p:PlatformToolset=$toolset ` 86 | /p:UseOfMfc=$mfc_type ` 87 | ${{env.SOLUTION_FILE_PATH}} 88 | 89 | New-Item -ItemType Directory build 90 | Move-Item -Path ".\$platform\${{ env.BUILD_CONFIGURATION }}\*.exe" ` 91 | -Destination ".\build\" 92 | 93 | - name: 🗜️ rename + optional upx 94 | shell: msys2 {0} 95 | run: | 96 | set -ex 97 | 98 | cd "build" 99 | if [[ "${{ matrix.build_config }}" =~ ".static." ]]; then 100 | upx -9 *.exe 101 | fi 102 | cp "ducode.exe" "ducode.${{ matrix.build_config }}.exe" 103 | 104 | - name: 🗜️ archive binaries 105 | uses: actions/upload-artifact@v3 106 | with: 107 | name: binaries 108 | path: build/ducode.${{ matrix.build_config }}.exe 109 | 110 | - name: 📦 package for release 111 | if: startsWith(github.ref, 'refs/tags/v') 112 | shell: msys2 {0} 113 | run: | 114 | set -ex 115 | 116 | cd "build" 117 | GIT_VERSION="${{github.ref}}" 118 | GIT_VERSION="${GIT_VERSION:10}" 119 | ZIP_NAME="ducode.${{ matrix.build_config }}.${GIT_VERSION}.zip" 120 | zip -9 "../${ZIP_NAME}" "ducode.exe" 121 | 122 | - name: 📝 draft release 123 | if: startsWith(github.ref, 'refs/tags/v') 124 | uses: softprops/action-gh-release@v1 125 | env: 126 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 127 | with: 128 | body: "" 129 | draft: true 130 | # note you'll typically need to create a personal access token 131 | # with permissions to create releases in the other repo 132 | token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} 133 | files: | 134 | *.zip 135 | -------------------------------------------------------------------------------- /generator/generator.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {d8e86c5e-c09b-40e0-9359-225308efac4d} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Components 56 | 57 | 58 | Components 59 | 60 | 61 | Components 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | 71 | 72 | Components 73 | 74 | 75 | Components 76 | 77 | 78 | Components 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | Source Files 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files 106 | 107 | 108 | Source Files 109 | 110 | 111 | 112 | 113 | Resource Files 114 | 115 | 116 | 117 | 118 | Resource Files 119 | 120 | 121 | 122 | 123 | Resource Files 124 | 125 | 126 | Resource Files 127 | 128 | 129 | -------------------------------------------------------------------------------- /generator/ProgressText.cpp: -------------------------------------------------------------------------------- 1 | // ProgressText.cpp : implementation file 2 | // 3 | 4 | #include "stdafx.h" 5 | 6 | #include "generator.h" 7 | #include "ProgressText.h" 8 | #include "DPISupport.h" 9 | 10 | void inline shrink(CRect* rect) { 11 | rect->left++; 12 | rect->top++; 13 | rect->right--; 14 | rect->bottom--; 15 | } 16 | 17 | // CProgressText 18 | 19 | IMPLEMENT_DYNAMIC(CProgressText, CProgressCtrl) 20 | 21 | CProgressText::CProgressText() 22 | { 23 | Init(); 24 | } 25 | 26 | CProgressText::~CProgressText() 27 | { 28 | SAFE_DELETE(text); 29 | } 30 | 31 | void CProgressText::Init() 32 | { 33 | text = new CString(""); 34 | 35 | progressForegroundBrush = new CBrush(progressForegroundColour); 36 | progressBackgroundBrush = new CBrush(progressBackgroundColour); 37 | componentBorderBrush = new CBrush(componentBorderColour); 38 | 39 | font = new CFont(); 40 | } 41 | 42 | void CProgressText::SetCurrent(uint current) 43 | { 44 | this->current = current; 45 | this->SetPos(this->current); 46 | this->UpdateText(); 47 | } 48 | 49 | void CProgressText::SetMax(uint max) 50 | { 51 | this->max = max; 52 | this->SetRange32(0, max); 53 | this->UpdateText(); 54 | } 55 | 56 | void CProgressText::Increase() 57 | { 58 | this->current++; 59 | this->SetPos(this->current); 60 | this->UpdateText(); 61 | } 62 | 63 | void CProgressText::Reset() 64 | { 65 | this->current = 0; 66 | this->max = 0; 67 | this->SetPos(0); 68 | this->UpdateText(); 69 | this->RedrawWindow(); 70 | } 71 | 72 | void CProgressText::UpdateText() { 73 | if (this->max == 0) { 74 | this->percent = 0; 75 | this->text->SetString(_T("")); 76 | } 77 | else 78 | { 79 | this->percent = double(this->current) / double(this->max); 80 | this->text->Format(_T("%d / %d (%.2f%%)"), this->current, this->max, this->percent * 100); 81 | } 82 | } 83 | 84 | 85 | BEGIN_MESSAGE_MAP(CProgressText, CProgressCtrl) 86 | ON_WM_PAINT() 87 | ON_WM_ERASEBKGND() 88 | END_MESSAGE_MAP() 89 | 90 | 91 | 92 | // CProgressText message handlers 93 | 94 | 95 | 96 | 97 | void CProgressText::OnPaint() 98 | { 99 | CPaintDC dc(this); // device context for painting 100 | // Do not call CProgressCtrl::OnPaint() for painting messages 101 | 102 | double dpi_scale = double(DPISupport::GetWindowDPI(GetSafeHwnd())) / 96.0; 103 | 104 | CRect rect; 105 | GetClientRect(&rect); 106 | 107 | CDC memDC; 108 | memDC.CreateCompatibleDC(&dc); 109 | memDC.SetMapMode(dc.GetMapMode()); 110 | memDC.SetViewportOrg(dc.GetViewportOrg()); 111 | memDC.IntersectClipRect(rect); 112 | 113 | CBitmap bmp; 114 | bmp.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); 115 | auto pOldBmp = memDC.SelectObject(&bmp); 116 | 117 | memDC.SetBkMode(TRANSPARENT); 118 | 119 | // 绘制一圈边框 120 | memDC.SelectObject(GetStockObject(DC_PEN)); 121 | memDC.SetDCPenColor(this->componentBorderColour); 122 | memDC.Rectangle(rect); 123 | 124 | // 绘制四方形 125 | { 126 | auto rectProgress = rect; 127 | shrink(&rectProgress); 128 | memDC.FillRect(rectProgress, this->progressBackgroundBrush); 129 | 130 | rectProgress.right = rectProgress.left + LONG(rectProgress.Width() * this->percent); 131 | memDC.FillRect(rectProgress, this->progressForegroundBrush); 132 | } 133 | 134 | auto font_height = static_cast((rect.Height() - 4) * dpi_scale * 0.75); 135 | font->DeleteObject(); 136 | VERIFY(font->CreateFont( 137 | font_height, // nHeight 138 | 0, // nWidth 139 | 0, // nEscapement 140 | 0, // nOrientation 141 | FW_NORMAL, // nWeight 142 | FALSE, // bItalic 143 | FALSE, // bUnderline 144 | 0, // cStrikeOut 145 | ANSI_CHARSET, // nCharSet 146 | OUT_DEFAULT_PRECIS, // nOutPrecision 147 | CLIP_DEFAULT_PRECIS, // nClipPrecision 148 | DEFAULT_QUALITY, // nQuality 149 | DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily 150 | _T("Consolas"))); // lpszFacename 151 | 152 | auto def_font = memDC.SelectObject(font); 153 | auto size = memDC.GetTextExtent(*text); 154 | auto x = rect.right - size.cx - 8; 155 | auto y = (rect.Height() - size.cy) / 2; 156 | memDC.TextOut(x, y, *text, this->text->GetLength()); 157 | memDC.SelectObject(def_font); 158 | 159 | // 拷贝图片 160 | dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, rect.left, rect.top, SRCCOPY); 161 | 162 | // 还原选择的对象 163 | memDC.SelectObject(pOldBmp); 164 | } 165 | 166 | // ReSharper disable once CppMemberFunctionMayBeStatic 167 | // ReSharper disable once CppMemberFunctionMayBeConst 168 | BOOL CProgressText::OnEraseBkgnd(CDC* pDC) 169 | { 170 | return TRUE; 171 | } 172 | -------------------------------------------------------------------------------- /generator/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | MICROSOFT FOUNDATION CLASS LIBRARY : generator Project Overview 3 | =============================================================================== 4 | 5 | The application wizard has created this generator application for 6 | you. This application not only demonstrates the basics of using the Microsoft 7 | Foundation Classes but is also a starting point for writing your application. 8 | 9 | This file contains a summary of what you will find in each of the files that 10 | make up your generator application. 11 | 12 | generator.vcxproj 13 | This is the main project file for VC++ projects generated using an application wizard. 14 | It contains information about the version of Visual C++ that generated the file, and 15 | information about the platforms, configurations, and project features selected with the 16 | application wizard. 17 | 18 | generator.vcxproj.filters 19 | This is the filters file for VC++ projects generated using an Application Wizard. 20 | It contains information about the assciation between the files in your project 21 | and the filters. This association is used in the IDE to show grouping of files with 22 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 23 | "Source Files" filter). 24 | 25 | generator.h 26 | This is the main header file for the application. It includes other 27 | project specific headers (including Resource.h) and declares the 28 | CApp application class. 29 | 30 | generator.cpp 31 | This is the main application source file that contains the application 32 | class CApp. 33 | 34 | generator.rc 35 | This is a listing of all of the Microsoft Windows resources that the 36 | program uses. It includes the icons, bitmaps, and cursors that are stored 37 | in the RES subdirectory. This file can be directly edited in Microsoft 38 | Visual C++. Your project resources are in 1033. 39 | 40 | res\generator.ico 41 | This is an icon file, which is used as the application's icon. This 42 | icon is included by the main resource file generator.rc. 43 | 44 | res\generator.rc2 45 | This file contains resources that are not edited by Microsoft 46 | Visual C++. You should place all resources not editable by 47 | the resource editor in this file. 48 | 49 | 50 | ///////////////////////////////////////////////////////////////////////////// 51 | 52 | The application wizard creates one dialog class: 53 | 54 | appDlg.h, appDlg.cpp - the dialog 55 | These files contain your CAppDlg class. This class defines 56 | the behavior of your application's main dialog. The dialog's template is 57 | in generator.rc, which can be edited in Microsoft Visual C++. 58 | 59 | ///////////////////////////////////////////////////////////////////////////// 60 | 61 | Help Support: 62 | 63 | hlp\generator.hhp 64 | This file is a help project file. It contains the data needed to 65 | compile the help files into a .chm file. 66 | 67 | hlp\generator.hhc 68 | This file lists the contents of the help project. 69 | 70 | hlp\generator.hhk 71 | This file contains an index of the help topics. 72 | 73 | hlp\afxcore.htm 74 | This file contains the standard help topics for standard MFC 75 | commands and screen objects. Add your own help topics to this file. 76 | 77 | makehtmlhelp.bat 78 | This file is used by the build system to compile the help files. 79 | 80 | hlp\Images\*.gif 81 | These are bitmap files required by the standard help file topics for 82 | Microsoft Foundation Class Library standard commands. 83 | 84 | 85 | ///////////////////////////////////////////////////////////////////////////// 86 | 87 | Other standard files: 88 | 89 | StdAfx.h, StdAfx.cpp 90 | These files are used to build a precompiled header (PCH) file 91 | named generator.pch and a precompiled types file named StdAfx.obj. 92 | 93 | Resource.h 94 | This is the standard header file, which defines new resource IDs. 95 | Microsoft Visual C++ reads and updates this file. 96 | 97 | generator.manifest 98 | Application manifest files are used by Windows XP to describe an applications 99 | dependency on specific versions of Side-by-Side assemblies. The loader uses this 100 | information to load the appropriate assembly from the assembly cache or private 101 | from the application. The Application manifest maybe included for redistribution 102 | as an external .manifest file that is installed in the same folder as the application 103 | executable or it may be included in the executable in the form of a resource. 104 | ///////////////////////////////////////////////////////////////////////////// 105 | 106 | Other notes: 107 | 108 | The application wizard uses "TODO:" to indicate parts of the source code you 109 | should add to or customize. 110 | 111 | If your application uses MFC in a shared DLL, you will need 112 | to redistribute the MFC DLLs. If your application is in a language 113 | other than the operating system's locale, you will also have to 114 | redistribute the corresponding localized resources MFC100XXX.DLL. 115 | For more information on both of these topics, please see the section on 116 | redistributing Visual C++ applications in MSDN documentation. 117 | 118 | ///////////////////////////////////////////////////////////////////////////// 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | *.tlog 263 | 264 | vs2019_ci_static_xp/ 265 | -------------------------------------------------------------------------------- /generator/appDlg.cpp: -------------------------------------------------------------------------------- 1 | 2 | // appDlg.cpp : implementation file 3 | // 4 | 5 | #include "stdafx.h" 6 | #include "generator.h" 7 | #include "appDlg.h" 8 | #include "afxdialogex.h" 9 | #include "utils.h" 10 | #include "base64.h" 11 | #include "ClipboardHelper.h" 12 | 13 | #ifdef _DEBUG 14 | #define new DEBUG_NEW 15 | #endif 16 | 17 | // CAboutDlg dialog used for App About 18 | 19 | class CAboutDlg : public CDialogEx 20 | { 21 | public: 22 | CAboutDlg(); 23 | 24 | // Dialog Data 25 | #ifdef AFX_DESIGN_TIME 26 | enum { IDD = IDD_ABOUTBOX }; 27 | #endif 28 | 29 | protected: 30 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 31 | 32 | // Implementation 33 | protected: 34 | DECLARE_MESSAGE_MAP() 35 | public: 36 | // void AddFile(CString& srcDir, CString& filename); 37 | }; 38 | 39 | CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) 40 | { 41 | } 42 | 43 | void CAboutDlg::DoDataExchange(CDataExchange* pDX) 44 | { 45 | CDialogEx::DoDataExchange(pDX); 46 | } 47 | 48 | BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) 49 | END_MESSAGE_MAP() 50 | 51 | 52 | // CAppDlg dialog 53 | 54 | 55 | 56 | CAppDlg::CAppDlg(CWnd* pParent /*=NULL*/) 57 | : CDialogEx(IDD_GENERATOR_DIALOG, pParent) 58 | { 59 | m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 60 | } 61 | 62 | void CAppDlg::DoDataExchange(CDataExchange* pDX) 63 | { 64 | CDialogEx::DoDataExchange(pDX); 65 | DDX_Control(pDX, IDC_PROG_FILE, m_progFile); 66 | DDX_Control(pDX, IDC_PROG_ALL, m_progAll); 67 | DDX_Control(pDX, IDC_LIST_FILES, m_listFiles); 68 | DDX_Control(pDX, IDC_EDIT_OUTPUT, m_editOutput); 69 | DDX_Control(pDX, IDC_CHK_RECURSIVE, m_chkRecursive); 70 | DDX_Control(pDX, IDC_CHK_URL, m_chkUrl); 71 | DDX_Control(pDX, IDC_SYSLINK_BLOG, m_linkBlog); 72 | DDX_Control(pDX, IDC_BTN_ADD_DIR, m_btnAddDir); 73 | DDX_Control(pDX, IDC_BTN_ADD_FILE, m_btnAddFile); 74 | DDX_Control(pDX, IDC_BTN_CLEAR, m_btnClear); 75 | DDX_Control(pDX, IDC_BTN_COPY, m_btnCopy); 76 | DDX_Control(pDX, IDC_BTN_GENERATE, m_btnGenerate); 77 | } 78 | 79 | // NM_CLICK = -2 80 | constexpr UINT NM_CLICK_WITHOUT_WARNING = UINT(-2); 81 | 82 | BEGIN_MESSAGE_MAP(CAppDlg, CDialogEx) 83 | ON_WM_SYSCOMMAND() 84 | ON_WM_PAINT() 85 | ON_WM_QUERYDRAGICON() 86 | ON_BN_CLICKED(IDC_BTN_GENERATE, &CAppDlg::OnBnClickedGenerate) 87 | ON_BN_CLICKED(IDC_BTN_ADD_DIR, &CAppDlg::OnClickedBtnAddDir) 88 | ON_BN_CLICKED(IDC_BTN_ADD_FILE, &CAppDlg::OnClickedBtnAddFile) 89 | ON_BN_CLICKED(IDC_BTN_CLEAR, &CAppDlg::OnBnClickedBtnClear) 90 | ON_WM_DROPFILES() 91 | ON_NOTIFY(NM_CLICK_WITHOUT_WARNING, IDC_SYSLINK_BLOG, &CAppDlg::OnClickSyslinkBlog) 92 | ON_BN_CLICKED(IDC_BTN_COPY, &CAppDlg::OnBnClickedBtnCopy) 93 | END_MESSAGE_MAP() 94 | 95 | 96 | // CAppDlg message handlers 97 | 98 | BOOL CAppDlg::OnInitDialog() 99 | { 100 | CDialogEx::OnInitDialog(); 101 | 102 | AppendVersionNumber(); 103 | 104 | // Set the icon for this dialog. The framework does this automatically 105 | // when the application's main window is not a dialog 106 | SetIcon(m_hIcon, TRUE); // Set big icon 107 | SetIcon(m_hIcon, FALSE); // Set small icon 108 | 109 | CRegKey reg; 110 | if (reg.Open(HKEY_CURRENT_USER, _T("Software\\Jixun.Moe\\DuGenerator"), KEY_READ) == ERROR_SUCCESS) 111 | { 112 | DWORD dwLang; 113 | if (reg.QueryDWORDValue(_T("LANG_ID"), dwLang) == ERROR_SUCCESS) 114 | { 115 | SetThreadUILanguage(LANGID(dwLang)); 116 | } 117 | 118 | reg.Close(); 119 | } 120 | 121 | std::ignore = m_ofn_file_title.LoadString(IDS_PICK_FILE); 122 | std::ignore = m_ofn_dir_title.LoadString(IDS_PICK_DIR); 123 | 124 | m_progFile.SetRange(0, 10000); 125 | 126 | // 初始化目录选择 127 | std::ignore = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 128 | 129 | m_editOutput.SetLimitText(0); 130 | 131 | #if _DEBUG 132 | { 133 | char buffer[32]; 134 | sprintf_s(buffer, sizeof(buffer), "text limit: 0x%08x\n", m_editOutput.GetLimitText()); 135 | OutputDebugStringA(buffer); 136 | } 137 | #endif 138 | 139 | return TRUE; // return TRUE unless you set the focus to a control 140 | } 141 | 142 | void CAppDlg::OnSysCommand(UINT nID, LPARAM lParam) 143 | { 144 | if ((nID & 0xFFF0) == IDM_ABOUTBOX) 145 | { 146 | CAboutDlg dlgAbout; 147 | dlgAbout.DoModal(); 148 | } 149 | else 150 | { 151 | CDialogEx::OnSysCommand(nID, lParam); 152 | } 153 | } 154 | 155 | // If you add a minimize button to your dialog, you will need the code below 156 | // to draw the icon. For MFC applications using the document/view model, 157 | // this is automatically done for you by the framework. 158 | 159 | void CAppDlg::OnPaint() 160 | { 161 | if (IsIconic()) 162 | { 163 | CPaintDC dc(this); // device context for painting 164 | 165 | SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); 166 | 167 | // Center icon in client rectangle 168 | int cxIcon = GetSystemMetrics(SM_CXICON); 169 | int cyIcon = GetSystemMetrics(SM_CYICON); 170 | CRect rect; 171 | GetClientRect(&rect); 172 | int x = (rect.Width() - cxIcon + 1) / 2; 173 | int y = (rect.Height() - cyIcon + 1) / 2; 174 | 175 | // Draw the icon 176 | dc.DrawIcon(x, y, m_hIcon); 177 | } 178 | else 179 | { 180 | CDialogEx::OnPaint(); 181 | } 182 | } 183 | 184 | // The system calls this function to obtain the cursor to display while the user drags 185 | // the minimized window. 186 | HCURSOR CAppDlg::OnQueryDragIcon() 187 | { 188 | return static_cast(m_hIcon); 189 | } 190 | 191 | void WINAPI _thread_process_file(CAppDlg* app) 192 | { 193 | app->ProcessFiles(); 194 | } 195 | 196 | 197 | void CAppDlg::OnBnClickedGenerate() 198 | { 199 | CreateThread(nullptr, 0, LPTHREAD_START_ROUTINE(_thread_process_file), this, 0, nullptr); 200 | } 201 | 202 | void cb_add_file(const CString &srcDir, const CString &filename, void* extra) 203 | { 204 | static_cast(extra)->AddFile(srcDir, filename); 205 | } 206 | 207 | void CAppDlg::OnClickedBtnAddDir() 208 | { 209 | auto dirs = OpenDirectoryDialog(m_ofn_dir_title, GetSafeHwnd()); 210 | for(auto& dir : dirs) 211 | { 212 | EnumFiles(*dir, BST_CHECKED == m_chkRecursive.GetCheck(), cb_add_file, this); 213 | delete dir; 214 | } 215 | } 216 | 217 | 218 | void CAppDlg::OnClickedBtnAddFile() 219 | { 220 | auto v = OpenFileDialog(this->m_ofn_file_title, GetSafeHwnd()); 221 | for(auto file : v) 222 | { 223 | CString path(*file); 224 | auto pos = path.ReverseFind(_T('\\')); 225 | m_listFiles.AddItem(path.Left(pos), path.Right(path.GetLength() - pos - 1)); 226 | delete file; 227 | } 228 | } 229 | 230 | void CAppDlg::AddFile(const CString& srcDir, const CString& filename) 231 | { 232 | m_listFiles.AddItem(srcDir, filename); 233 | } 234 | 235 | void _proc_file_callback(ProcType type, double progress, CFileItem* lpItem, void* extra) 236 | { 237 | static_cast(extra)->ProcFile(type, progress, lpItem); 238 | } 239 | 240 | void CAppDlg::ProcessFiles() 241 | { 242 | std::lock_guard guard(mutex); 243 | 244 | OutputDebugString(_T("Begin read file...")); 245 | m_progAll.SetMax(m_listFiles.GetCount()); 246 | m_progAll.SetCurrent(0); 247 | m_progFile.SetPos(0); 248 | 249 | m_btnAddDir.EnableWindow(FALSE); 250 | m_btnAddFile.EnableWindow(FALSE); 251 | m_btnCopy.EnableWindow(FALSE); 252 | m_btnClear.EnableWindow(FALSE); 253 | m_btnGenerate.EnableWindow(FALSE); 254 | 255 | m_listFiles.ProcessFiles(_proc_file_callback, this); 256 | 257 | m_btnAddDir.EnableWindow(TRUE); 258 | m_btnAddFile.EnableWindow(TRUE); 259 | m_btnCopy.EnableWindow(TRUE); 260 | m_btnClear.EnableWindow(TRUE); 261 | m_btnGenerate.EnableWindow(TRUE); 262 | } 263 | 264 | void CAppDlg::AddHashEntry(CFileItem* data) 265 | { 266 | if (m_editOutput.GetWindowTextLengthW() > 0) { 267 | m_editOutput.Append(_T("\r\n")); 268 | } 269 | 270 | if (m_chkUrl.GetCheck() == BST_CHECKED) { 271 | m_editOutput.Append(data->BDLink()); 272 | } 273 | else { 274 | m_editOutput.Append(data->DownloadCode()); 275 | } 276 | } 277 | 278 | void CAppDlg::ProcFile(ProcType proc, double progress, CFileItem* lp_item) 279 | { 280 | switch(proc) 281 | { 282 | case ProcType::FILE_PROG: 283 | m_progFile.SetPos(int(progress * 10000)); 284 | break; 285 | 286 | case ProcType::INC_FILE: 287 | m_progFile.SetPos(0); 288 | m_progAll.Increase(); 289 | AddHashEntry(lp_item); 290 | break; 291 | 292 | case ProcType::ERR_FILE: 293 | OutputDebugString(_T("Failed to process file.\n")); 294 | m_progAll.Increase(); 295 | break; 296 | 297 | default: ; 298 | } 299 | } 300 | 301 | void CAppDlg::RealExit() 302 | { 303 | m_listFiles.StopProcessing(); 304 | 305 | // TODO: Clean up. 306 | 307 | CDialogEx::OnCancel(); 308 | } 309 | 310 | void CAppDlg::AppendVersionNumber() 311 | { 312 | CString csEntry; 313 | HMODULE hLib = AfxGetResourceHandle(); 314 | 315 | HRSRC hVersion = FindResource(hLib, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); 316 | if (hVersion == nullptr) return; 317 | 318 | HGLOBAL hGlobal = LoadResource(hLib, hVersion); 319 | if (hGlobal == nullptr) { 320 | FreeResource(hVersion); 321 | return; 322 | } 323 | 324 | LPVOID versionInfo = LockResource(hGlobal); 325 | if (versionInfo == nullptr) { 326 | UnlockResource(hGlobal); 327 | FreeResource(hVersion); 328 | return; 329 | } 330 | 331 | LPVOID lpBuffer = nullptr; 332 | UINT dwCharCount = 0; 333 | 334 | if (VerQueryValue(versionInfo, L"\\StringFileInfo\\080004B0\\FileVersion", &lpBuffer, &dwCharCount)) { 335 | CString strTitle; 336 | GetWindowText(strTitle); 337 | 338 | auto strVersionString = new TCHAR[dwCharCount + 1]; 339 | memcpy(strVersionString, lpBuffer, dwCharCount * sizeof(TCHAR)); 340 | strVersionString[dwCharCount] = 0; 341 | 342 | auto pLastDot = _tcsrchr(strVersionString, _T('.')); 343 | if (pLastDot != nullptr) { 344 | *pLastDot = 0; 345 | } 346 | 347 | strTitle += _T(" (v"); 348 | strTitle += strVersionString; 349 | strTitle += _T(")"); 350 | SetWindowText(strTitle); 351 | 352 | delete[] strVersionString; 353 | } 354 | 355 | UnlockResource(hGlobal); 356 | FreeResource(hVersion); 357 | } 358 | 359 | DWORD WINAPI _thread_stop_process(void* param) 360 | { 361 | static_cast(param)->RealExit(); 362 | return 0; 363 | } 364 | 365 | void CAppDlg::OnCancel() 366 | { 367 | // 若为 ESC 导致的关闭窗口,无视它。 368 | if ((GetKeyState(VK_ESCAPE) & 0x8000) != 0) { 369 | return; 370 | } 371 | 372 | CreateThread(nullptr, 0, _thread_stop_process, this, 0, nullptr); 373 | } 374 | 375 | 376 | void CAppDlg::OnBnClickedBtnClear() 377 | { 378 | m_listFiles.ResetContent(); 379 | m_progAll.Reset(); 380 | m_editOutput.SetWindowText(_T("")); 381 | } 382 | 383 | 384 | void CAppDlg::OnDropFiles(HDROP hDropInfo) 385 | { 386 | CString path; 387 | 388 | int nFilesDropped = DragQueryFile(hDropInfo, 0xFFFFFFFF, nullptr, 0); 389 | 390 | for (int i = 0; iitem.szUrl, nullptr, nullptr, SW_SHOWNORMAL); 421 | *pResult = 0; 422 | } 423 | 424 | 425 | // ReSharper disable once CppMemberFunctionMayBeConst 426 | void CAppDlg::OnBnClickedBtnCopy() 427 | { 428 | CString str; 429 | m_editOutput.GetWindowText(str); 430 | CopyStringToClipboard(str, GetSafeHwnd()); 431 | } 432 | -------------------------------------------------------------------------------- /generator/generator.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {52BE8E83-C47E-469F-92B7-F078C51C0EBD} 24 | generator 25 | MFCProj 26 | ducode 27 | 10.0 28 | 29 | 30 | 31 | Application 32 | true 33 | v143 34 | Unicode 35 | Static 36 | 37 | 38 | Application 39 | false 40 | v143 41 | true 42 | Unicode 43 | Dynamic 44 | 45 | 46 | Application 47 | true 48 | v143 49 | Unicode 50 | Static 51 | 52 | 53 | Application 54 | false 55 | v143 56 | true 57 | Unicode 58 | Dynamic 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | true 80 | $(SolutionDir)$(PlatformTarget)\$(Configuration)\ 81 | 82 | 83 | true 84 | $(SolutionDir)$(PlatformTarget)\$(Configuration)\ 85 | 86 | 87 | false 88 | $(SolutionDir)$(PlatformTarget)\$(Configuration)\ 89 | 90 | 91 | false 92 | $(SolutionDir)$(PlatformTarget)\$(Configuration)\ 93 | 94 | 95 | 96 | Use 97 | Level3 98 | Disabled 99 | WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) 100 | true 101 | 102 | 103 | Windows 104 | Version.lib;%(AdditionalDependencies) 105 | 106 | 107 | false 108 | true 109 | _DEBUG;%(PreprocessorDefinitions) 110 | 111 | 112 | 0x0409 113 | _DEBUG;%(PreprocessorDefinitions) 114 | $(IntDir);%(AdditionalIncludeDirectories) 115 | 116 | 117 | PerMonitorHighDPIAware 118 | 119 | 120 | 121 | 122 | Use 123 | Level3 124 | Disabled 125 | _WINDOWS;_DEBUG;%(PreprocessorDefinitions) 126 | true 127 | 128 | 129 | Windows 130 | Version.lib;%(AdditionalDependencies) 131 | 132 | 133 | false 134 | true 135 | _DEBUG;%(PreprocessorDefinitions) 136 | 137 | 138 | 0x0409 139 | _DEBUG;%(PreprocessorDefinitions) 140 | $(IntDir);%(AdditionalIncludeDirectories) 141 | 142 | 143 | PerMonitorHighDPIAware 144 | 145 | 146 | 147 | 148 | Level3 149 | Use 150 | MaxSpeed 151 | true 152 | true 153 | WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) 154 | true 155 | MultiThreadedDLL 156 | 157 | 158 | Windows 159 | true 160 | true 161 | Version.lib;%(AdditionalDependencies) 162 | 163 | 164 | false 165 | true 166 | NDEBUG;%(PreprocessorDefinitions) 167 | 168 | 169 | 0x0409 170 | NDEBUG;%(PreprocessorDefinitions) 171 | $(IntDir);%(AdditionalIncludeDirectories) 172 | 173 | 174 | PerMonitorHighDPIAware 175 | 176 | 177 | 178 | 179 | Level3 180 | Use 181 | MaxSpeed 182 | true 183 | true 184 | _WINDOWS;NDEBUG;%(PreprocessorDefinitions) 185 | true 186 | MultiThreadedDLL 187 | 188 | 189 | Windows 190 | true 191 | true 192 | Version.lib;%(AdditionalDependencies) 193 | 194 | 195 | false 196 | true 197 | NDEBUG;%(PreprocessorDefinitions) 198 | 199 | 200 | 0x0409 201 | NDEBUG;%(PreprocessorDefinitions) 202 | $(IntDir);%(AdditionalIncludeDirectories) 203 | 204 | 205 | PerMonitorHighDPIAware 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | Create 242 | Create 243 | Create 244 | Create 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /generator/ListBoxEx.cpp: -------------------------------------------------------------------------------- 1 | // ListBoxEx.cpp : implementation file 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "ListBoxEx.h" 6 | #include "resource.h" 7 | #include "Hasher.h" 8 | #include "ClipboardHelper.h" 9 | #include "DPISupport.h" 10 | 11 | #include // std::ifstream 12 | 13 | #define BD_FILE_HEADER_SIZE (256 * 1024) 14 | constexpr double icon_base_dimention = 36.0; 15 | 16 | // CListBoxEx 17 | 18 | IMPLEMENT_DYNAMIC(CListBoxEx, CListBox) 19 | HICON CListBoxEx::m_hIconTick = nullptr; 20 | CString CListBoxEx::m_sPlaceholder; 21 | HFONT CListBoxEx::m_systemFont; 22 | CListBoxEx::CListBoxEx() 23 | { 24 | // init guard 25 | if (m_hIconTick == nullptr) 26 | { 27 | m_hIconTick = static_cast(LoadImage(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICON_TICK), IMAGE_ICON, 16, 16, 0)); 28 | int _ = m_sPlaceholder.LoadStringW(IDS_LIST_PLACEHOLDER); 29 | 30 | NONCLIENTMETRICS metrics = {}; 31 | metrics.cbSize = sizeof(metrics); 32 | SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0); 33 | m_systemFont = CreateFontIndirect(&metrics.lfCaptionFont); 34 | CString placeholder; 35 | } 36 | } 37 | 38 | CListBoxEx::~CListBoxEx() 39 | { 40 | } 41 | 42 | 43 | BEGIN_MESSAGE_MAP(CListBoxEx, CListBox) 44 | ON_WM_VKEYTOITEM_REFLECT() 45 | ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText) 46 | ON_WM_PAINT() 47 | END_MESSAGE_MAP() 48 | 49 | 50 | 51 | // CListBoxEx message handlers 52 | void CListBoxEx::GetVisibleRange(int& s, int& e) 53 | { 54 | auto maxIndex = this->GetCount() - 1; 55 | e = s = GetTopIndex(); 56 | RECT rect; 57 | this->GetClientRect(&rect); 58 | auto height = rect.bottom - rect.top; 59 | auto currentHeight = 0; 60 | 61 | while(e < maxIndex) 62 | { 63 | currentHeight += GetItemHeight(e); 64 | if (currentHeight >= height) break; 65 | e++; 66 | } 67 | } 68 | 69 | bool CListBoxEx::ItemIsVisible(int nIndex) 70 | { 71 | int s, e; 72 | GetVisibleRange(s, e); 73 | return s <= nIndex && nIndex <= e; 74 | } 75 | 76 | bool CListBoxEx::RedrawIfVisible(int i) 77 | { 78 | RECT rectItem; 79 | if (this->GetItemRect(i, &rectItem) != LB_ERR) 80 | { 81 | if (rectItem.bottom < 0) return false; 82 | 83 | RECT rectCtrl; 84 | this->GetClientRect(&rectCtrl); 85 | 86 | if (rectItem.top <= rectCtrl.bottom - rectCtrl.top) 87 | { 88 | this->InvalidateRect(&rectItem, FALSE); 89 | return true; 90 | } 91 | } 92 | 93 | return false; 94 | } 95 | 96 | 97 | // 4MB Buffer 98 | #define BUF_SIZE (1024*1024*4) 99 | void CListBoxEx::ProcessFiles(f_proc_file_callback cb, void* extra) 100 | { 101 | std::lock_guard guard(mutex); 102 | m_bStop = false; 103 | 104 | auto c = GetCount(); 105 | char* buffer = new char[BUF_SIZE]; 106 | Hasher hash(CALG_MD5); 107 | for(auto i = 0; i < c; i++) 108 | { 109 | auto data = reinterpret_cast(GetItemData(i)); 110 | if (data->Done()) 111 | { 112 | cb(ProcType::INC_FILE, 0, data, extra); 113 | continue; 114 | } 115 | 116 | auto path(*data->m_pDirectory + _T("\\") + *data->m_pFilename); 117 | std::ifstream is(path, std::ifstream::binary); 118 | 119 | if (!is) 120 | { 121 | cb(ProcType::ERR_FILE, 0, nullptr, extra); 122 | is.close(); 123 | break; 124 | } 125 | 126 | is.read(buffer, BD_FILE_HEADER_SIZE); 127 | hash.Init(); 128 | hash.Feed(buffer, int(is.gcount())); 129 | data->m_pFirstHash = hash.GetHashStr(); 130 | 131 | // 已经读完了? 132 | if (is.eof()) { 133 | is.close(); 134 | 135 | // 更新完整哈希 136 | data->m_pFullHash = new CString(*data->m_pFirstHash); 137 | 138 | // 重绘 + 通知 139 | this->RedrawIfVisible(i); 140 | cb(ProcType::INC_FILE, 0, data, extra); 141 | continue; 142 | } 143 | 144 | is.seekg(0, SEEK_SET); 145 | hash.Init(); 146 | double step = double(BUF_SIZE) / data->m_nSize; 147 | double prog = 0; 148 | 149 | do 150 | { 151 | if (m_bStop) 152 | { 153 | is.close(); 154 | delete[] buffer; 155 | return; 156 | } 157 | 158 | is.read(buffer, BUF_SIZE); 159 | 160 | auto eof = is.eof(); 161 | if (!eof && is.fail()) 162 | { 163 | #ifdef _DEBUG 164 | CString str; 165 | str.Format(_T("state: %x (goodbit: 0, eof: 1, fail: 2, badbit: 4)\n"), is.rdstate()); 166 | OutputDebugString(str); 167 | #endif 168 | cb(ProcType::ERR_FILE, 0, nullptr, extra); 169 | break; 170 | } 171 | 172 | hash.Feed(buffer, eof ? int(is.gcount()) : BUF_SIZE); 173 | 174 | if (eof) 175 | { 176 | data->m_pFullHash = hash.GetHashStr(); 177 | 178 | if (this->RedrawIfVisible(i)) 179 | { 180 | #ifdef _DEBUG 181 | OutputDebugString(_T("Redraw item.\n")); 182 | #endif 183 | 184 | } 185 | cb(ProcType::INC_FILE, 0, data, extra); 186 | break; 187 | } 188 | prog += step; 189 | cb(ProcType::FILE_PROG, prog, data, extra); 190 | } while (true); 191 | is.close(); 192 | } 193 | 194 | delete[] buffer; 195 | } 196 | 197 | void CListBoxEx::StopProcessing() 198 | { 199 | m_bStop = true; 200 | { 201 | std::lock_guard guard(mutex); 202 | } 203 | } 204 | 205 | void CListBoxEx::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) 206 | { 207 | uint32_t dpi = DPISupport::GetWindowDPI(GetSafeHwnd()); 208 | auto dpi_scale = static_cast(dpi) / 96.0; 209 | 210 | auto icon_height = static_cast(dpi_scale * icon_base_dimention); 211 | auto icon_top_margin = static_cast(dpi_scale * 8); 212 | 213 | lpMeasureItemStruct->itemHeight = icon_height + icon_top_margin * 2; 214 | } 215 | 216 | void CListBoxEx::DrawItemData(LPDRAWITEMSTRUCT lpDrawItemStruct, CFileItem* pItem) 217 | { 218 | uint32_t dpi = DPISupport::GetWindowDPI(GetSafeHwnd()); 219 | auto dpi_scale = static_cast(dpi) / 96.0; 220 | 221 | auto icon_width = static_cast(dpi_scale * icon_base_dimention); 222 | auto icon_height = static_cast(dpi_scale * icon_base_dimention); 223 | auto icon_tick_width = static_cast(dpi_scale * 16.0); 224 | auto icon_tick_height = static_cast(dpi_scale * 16.0); 225 | auto icon_right_margin = static_cast(dpi_scale * 8.0); 226 | auto item_padding = static_cast(dpi_scale * 4.0); 227 | auto text_line_margin = static_cast(dpi_scale * 2.0); 228 | 229 | auto pDC = CDC::FromHandle(lpDrawItemStruct->hDC); 230 | pDC->SetBkMode(TRANSPARENT); 231 | 232 | CRect rectItem(lpDrawItemStruct->rcItem); 233 | rectItem.DeflateRect(item_padding, item_padding, item_padding, item_padding); 234 | CRect rectIcon(rectItem.left, rectItem.top, rectItem.left + icon_width, rectItem.top + icon_height); 235 | CRect rectIconTick( 236 | rectIcon.left, 237 | rectIcon.top + icon_height - icon_tick_height, 238 | rectIcon.left + icon_tick_width, 239 | rectItem.top + icon_height - icon_tick_height 240 | ); 241 | CRect rectText(rectIcon.right + icon_right_margin, rectItem.top, rectItem.right - icon_right_margin * 2, rectItem.bottom); 242 | 243 | auto action = lpDrawItemStruct->itemAction; 244 | auto state = lpDrawItemStruct->itemState; 245 | 246 | bool selected = (state & ODS_SELECTED) && (action & (ODA_SELECT | ODA_DRAWENTIRE)); 247 | bool redraw = (action & ODA_DRAWENTIRE) || (!(state & ODS_SELECTED) && (action & ODA_SELECT)); 248 | 249 | auto textColour = selected ? m_textSelected : m_text; 250 | auto descTextColour = selected ? m_descTextSelected : m_descText; 251 | 252 | CBrush brushBackground(selected ? m_bgSelected : m_bgClear); 253 | CBrush brushText(textColour); 254 | CBrush brushDescText(descTextColour); 255 | 256 | if (selected || redraw) 257 | { 258 | CString str; 259 | pDC->FillRect(&lpDrawItemStruct->rcItem, &brushBackground); 260 | pDC->DrawIcon(rectIcon.left, rectIcon.top, pItem->m_hIcon); 261 | 262 | if (pItem->m_pFullHash) 263 | { 264 | DrawIconEx(*pDC, rectIconTick.left, rectIconTick.top, m_hIconTick, icon_tick_width, icon_tick_height, NULL, nullptr, DI_NORMAL); 265 | } 266 | 267 | auto rect = rectText; 268 | pDC->SetTextColor(textColour); 269 | rect.OffsetRect(text_line_margin, 0); 270 | 271 | str.SetString(*pItem->m_pFilename); 272 | pDC->DrawText(str, str.GetLength(), rect, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS); 273 | rect.OffsetRect(0, text_line_margin + pDC->GetTextExtent(str).cy); 274 | 275 | 276 | str.Format(IDS_DIR, *pItem->m_pDirectory); 277 | pDC->SetTextColor(descTextColour); 278 | pDC->DrawText(str, str.GetLength(), rect, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS); 279 | rect.OffsetRect(0, text_line_margin + pDC->GetTextExtent(str).cy); 280 | 281 | str.Format(IDS_FILE_SIZE, pItem->GetSizeString()); 282 | pDC->DrawText(str, str.GetLength(), rect, DT_LEFT | DT_SINGLELINE); 283 | rect.OffsetRect(0, text_line_margin + pDC->GetTextExtent(str).cy); 284 | } 285 | } 286 | 287 | void CListBoxEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 288 | { 289 | auto pData = GetItemDataPtr(lpDrawItemStruct->itemID); 290 | if (pData != (void*)-1) { 291 | this->DrawItemData(lpDrawItemStruct, reinterpret_cast(pData)); 292 | } 293 | 294 | } 295 | 296 | uint64_t FileSize(const wchar_t* name) 297 | { 298 | auto hFile = CreateFileW( 299 | name, GENERIC_READ, FILE_SHARE_READ, nullptr, 300 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr 301 | ); 302 | if (hFile == INVALID_HANDLE_VALUE) { 303 | return 0; // could not open file 304 | } 305 | LARGE_INTEGER fs_large = {0}; 306 | if (!GetFileSizeEx(hFile, &fs_large)) { 307 | // get file size failed 308 | fs_large.QuadPart = 0; 309 | } 310 | CloseHandle(hFile); 311 | 312 | return static_cast(fs_large.QuadPart); 313 | } 314 | 315 | int CListBoxEx::AddItem(const CString& srcDir, const CString& filename) 316 | { 317 | std::lock_guard guard(this->mutex); 318 | 319 | auto fullPath(srcDir + _T("\\") + filename); 320 | auto fSize = FileSize(fullPath); 321 | 322 | auto index = this->AddString(_T("")); 323 | auto data = new CFileItem(); 324 | data->m_pDirectory = new CString(srcDir); 325 | data->m_pFilename = new CString(filename); 326 | 327 | SHFILEINFO shfi = {}; 328 | SHGetFileInfo(fullPath, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(SHFILEINFO), 329 | SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SHELLICONSIZE); 330 | 331 | data->m_hIcon = shfi.hIcon; 332 | data->m_nSize = fSize; 333 | 334 | SetItemData(index, DWORD_PTR(data)); 335 | 336 | return 0; 337 | } 338 | 339 | void CListBoxEx::CopySelectedHashes() 340 | { 341 | CString str; 342 | 343 | for (int i = 0; i < GetCount(); i++) 344 | { 345 | if (GetSel(i)) 346 | { 347 | str.AppendFormat(L"%s\r\n", reinterpret_cast(GetItemData(i))->DownloadCode().GetString()); 348 | } 349 | } 350 | 351 | CopyStringToClipboard(str); 352 | } 353 | 354 | int CListBoxEx::VKeyToItem(UINT nKey, UINT nIndex) 355 | { 356 | // Returns –2 for no further action, 357 | // –1 for default action, or 358 | // a nonnegative number to specify an index of a 359 | // list box item on which to perform the default action for the keystroke. 360 | 361 | if(mutex.try_lock()) 362 | { 363 | switch (nKey) 364 | { 365 | case 'E': 366 | for (int i = 0; i < GetCount(); i++) 367 | SetSel(i, false); 368 | 369 | mutex.unlock(); 370 | return -2; 371 | 372 | case 'C': 373 | this->CopySelectedHashes(); 374 | mutex.unlock(); 375 | return -2; 376 | 377 | case 'D': 378 | case VK_DELETE: 379 | for (int i = GetCount() - 1; i >= 0; i--) 380 | { 381 | if (GetSel(i)) 382 | { 383 | DeleteString(i); 384 | SetSel(i, true); 385 | } 386 | } 387 | 388 | mutex.unlock(); 389 | return -2; 390 | 391 | } 392 | mutex.unlock(); 393 | return -1; 394 | } 395 | 396 | return -1; 397 | } 398 | 399 | 400 | void CListBoxEx::PreSubclassWindow() 401 | { 402 | CListBox::PreSubclassWindow(); 403 | EnableToolTips(TRUE); 404 | } 405 | 406 | INT_PTR CListBoxEx::OnToolHitTest(CPoint point, TOOLINFO* pTI) const 407 | { 408 | RECT itemRect; 409 | BOOL isOutside = FALSE; 410 | int row = ItemFromPoint(point, isOutside); 411 | if (row == -1 || isOutside) 412 | return -1; 413 | 414 | GetItemRect(row, &itemRect); 415 | 416 | pTI->rect = itemRect; 417 | pTI->hwnd = m_hWnd; 418 | pTI->uId = row; 419 | pTI->lpszText = LPSTR_TEXTCALLBACK; 420 | return pTI->uId; 421 | } 422 | 423 | BOOL CListBoxEx::OnToolTipText(UINT id, NMHDR * pNMHDR, LRESULT * pResult) 424 | { 425 | auto pToolTip = AfxGetModuleThreadState()->m_pToolTip; 426 | if (pToolTip) pToolTip->SetMaxTipWidth(SHRT_MAX); 427 | 428 | auto ptText = reinterpret_cast(pNMHDR); 429 | 430 | auto pItem = reinterpret_cast(this->GetItemData(static_cast(pNMHDR->idFrom))); 431 | 432 | CString str; 433 | str.Format(IDS_FILE_INFO, 434 | *pItem->m_pFilename, 435 | *pItem->m_pDirectory, 436 | pItem->GetSizeString()); 437 | 438 | 439 | auto strW = static_cast(str); 440 | auto memSize = str.GetLength() * sizeof TCHAR + (2 * sizeof TCHAR); 441 | auto lpszText = (TCHAR*)calloc(memSize, 1); 442 | if (!lpszText) { 443 | return FALSE; 444 | } 445 | 446 | memcpy(lpszText, strW, memSize); 447 | 448 | ptText->lpszText = lpszText; 449 | 450 | *pResult = 0; 451 | 452 | return TRUE; 453 | 454 | } 455 | 456 | void CListBoxEx::OnPaint() 457 | { 458 | if (GetCount() == 0) { 459 | CPaintDC dc(this); 460 | 461 | CRect rect; 462 | this->GetClientRect(rect); 463 | dc.SetTextColor(m_descText); 464 | dc.SelectObject(m_systemFont); 465 | dc.DrawText(m_sPlaceholder, m_sPlaceholder.GetLength(), rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); 466 | } 467 | else { 468 | Default(); 469 | } 470 | } 471 | --------------------------------------------------------------------------------