Repository: zhongyang219/TrafficMonitor Branch: master Commit: dd187f6001b1 Files: 279 Total size: 1.9 MB Directory structure: gitextract_ygmqo6_g/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── BugReport_en.yaml │ │ ├── BugReport_zh.yaml │ │ ├── FeatureRequest_en.yaml │ │ ├── FeatureRequest_zh.yaml │ │ └── config.yml │ └── workflows/ │ └── main.yml ├── .gitignore ├── Help.md ├── Help_en-us.md ├── LICENSE ├── LICENSE_CN ├── OpenHardwareMonitorApi/ │ ├── LibreHardwareMonitorLib.xml │ ├── OpenHardwareMonitorApi.vcxproj │ ├── OpenHardwareMonitorApi.vcxproj.filters │ ├── OpenHardwareMonitorImp.cpp │ ├── OpenHardwareMonitorImp.h │ ├── ReadMe.txt │ ├── Stdafx.cpp │ ├── Stdafx.h │ ├── UpdateVisitor.cpp │ ├── UpdateVisitor.h │ ├── app.rc │ └── resource.h ├── PluginDemo/ │ ├── CustomDrawItem.cpp │ ├── CustomDrawItem.h │ ├── DataManager.cpp │ ├── DataManager.h │ ├── OptionsDlg.cpp │ ├── OptionsDlg.h │ ├── PluginDemo.cpp │ ├── PluginDemo.h │ ├── PluginDemo.rc │ ├── PluginDemo.vcxproj │ ├── PluginDemo.vcxproj.filters │ ├── PluginSystemDate.cpp │ ├── PluginSystemDate.h │ ├── PluginSystemTime.cpp │ ├── PluginSystemTime.h │ ├── framework.h │ ├── pch.cpp │ ├── pch.h │ └── resource.h ├── README.md ├── README_en-us.md ├── TrafficMonitor/ │ ├── AboutDlg.cpp │ ├── AboutDlg.h │ ├── AdapterCommon.cpp │ ├── AdapterCommon.h │ ├── AppAlreadyRuningDlg.cpp │ ├── AppAlreadyRuningDlg.h │ ├── BaseDialog.cpp │ ├── BaseDialog.h │ ├── CAutoAdaptSettingsDlg.cpp │ ├── CAutoAdaptSettingsDlg.h │ ├── CMFCColorDialogEx.cpp │ ├── CMFCColorDialogEx.h │ ├── CSkinPreviewView.cpp │ ├── CSkinPreviewView.h │ ├── CTabCtrlEx.cpp │ ├── CTabCtrlEx.h │ ├── CVariant.cpp │ ├── CVariant.h │ ├── CalendarHelper.cpp │ ├── CalendarHelper.h │ ├── ClassicalTaskbarDlg.cpp │ ├── ClassicalTaskbarDlg.h │ ├── ColorSettingListCtrl.cpp │ ├── ColorSettingListCtrl.h │ ├── ColorStatic.cpp │ ├── ColorStatic.h │ ├── ComboBox2.cpp │ ├── ComboBox2.h │ ├── Common.cpp │ ├── Common.h │ ├── CommonData.cpp │ ├── CommonData.h │ ├── D2D1Support.cpp │ ├── D2D1Support.h │ ├── D3D10Support1.cpp │ ├── D3D10Support1.h │ ├── DCompositionSupport.cpp │ ├── DCompositionSupport.h │ ├── DisplayItem.cpp │ ├── DisplayItem.h │ ├── DisplayTextSettingDlg.cpp │ ├── DisplayTextSettingDlg.h │ ├── DllFunctions.cpp │ ├── DllFunctions.h │ ├── DrawCommon.cpp │ ├── DrawCommon.h │ ├── DrawCommonEx.cpp │ ├── DrawCommonEx.h │ ├── DrawCommonFactory.cpp │ ├── DrawCommonFactory.h │ ├── DrawTextManager.cpp │ ├── DrawTextManager.h │ ├── Dxgi1Support2.cpp │ ├── Dxgi1Support2.h │ ├── FileDialogEx.cpp │ ├── FileDialogEx.h │ ├── FilePathHelper.cpp │ ├── FilePathHelper.h │ ├── GeneralSettingsDlg.cpp │ ├── GeneralSettingsDlg.h │ ├── HResultException.cpp │ ├── HResultException.h │ ├── HighResolutionTimer.h │ ├── HistoryTrafficCalendarDlg.cpp │ ├── HistoryTrafficCalendarDlg.h │ ├── HistoryTrafficDlg.cpp │ ├── HistoryTrafficDlg.h │ ├── HistoryTrafficFile.cpp │ ├── HistoryTrafficFile.h │ ├── HistoryTrafficListCtrl.cpp │ ├── HistoryTrafficListCtrl.h │ ├── HistoryTrafficListDlg.cpp │ ├── HistoryTrafficListDlg.h │ ├── IDrawCommon.h │ ├── IconSelectDlg.cpp │ ├── IconSelectDlg.h │ ├── Image2DEffect.cpp │ ├── Image2DEffect.h │ ├── IniHelper.cpp │ ├── IniHelper.h │ ├── LinkStatic.cpp │ ├── LinkStatic.h │ ├── ListCtrlEx.cpp │ ├── ListCtrlEx.h │ ├── MainWndColorDlg.cpp │ ├── MainWndColorDlg.h │ ├── MainWndSettingsDlg.cpp │ ├── MainWndSettingsDlg.h │ ├── MessageDlg.cpp │ ├── MessageDlg.h │ ├── NetworkInfoDlg.cpp │ ├── NetworkInfoDlg.h │ ├── Nullable.hpp │ ├── OptionsDlg.cpp │ ├── OptionsDlg.h │ ├── PdhHardwareQuery/ │ │ ├── CPUUsage.cpp │ │ ├── CPUUsage.h │ │ ├── CpuFreq.cpp │ │ ├── CpuFreq.h │ │ ├── DiskUsage.cpp │ │ ├── DiskUsage.h │ │ ├── GpuUsage.cpp │ │ ├── GpuUsage.h │ │ ├── PdhQuery.cpp │ │ └── PdhQuery.h │ ├── PictureStatic.cpp │ ├── PictureStatic.h │ ├── PluginInfoDlg.cpp │ ├── PluginInfoDlg.h │ ├── PluginManager.cpp │ ├── PluginManager.h │ ├── PluginManagerDlg.cpp │ ├── PluginManagerDlg.h │ ├── PluginUpdateHelper.cpp │ ├── PluginUpdateHelper.h │ ├── ReadMe.txt │ ├── RenderAPISupport.h │ ├── SelectConnectionsDlg.cpp │ ├── SelectConnectionsDlg.h │ ├── SetItemOrderDlg.cpp │ ├── SetItemOrderDlg.h │ ├── SettingsHelper.cpp │ ├── SettingsHelper.h │ ├── SimpleXML.cpp │ ├── SimpleXML.h │ ├── SkinAutoAdaptSettingDlg.cpp │ ├── SkinAutoAdaptSettingDlg.h │ ├── SkinDlg.cpp │ ├── SkinDlg.h │ ├── SkinFile.cpp │ ├── SkinFile.h │ ├── SkinManager.cpp │ ├── SkinManager.h │ ├── SpinEdit.cpp │ ├── SpinEdit.h │ ├── StaticEx.cpp │ ├── StaticEx.h │ ├── StrTable.cpp │ ├── StrTable.h │ ├── SupportedRenderEnums.cpp │ ├── SupportedRenderEnums.h │ ├── TabDlg.cpp │ ├── TabDlg.h │ ├── TaskBarDlg.cpp │ ├── TaskBarDlg.h │ ├── TaskBarDlgDrawCommon.cpp │ ├── TaskBarDlgDrawCommon.h │ ├── TaskBarSettingsDlg.cpp │ ├── TaskBarSettingsDlg.h │ ├── TaskbarColorDlg.cpp │ ├── TaskbarColorDlg.h │ ├── TaskbarDefaultStyle.cpp │ ├── TaskbarDefaultStyle.h │ ├── TaskbarHelper.cpp │ ├── TaskbarHelper.h │ ├── TaskbarItemOrderHelper.cpp │ ├── TaskbarItemOrderHelper.h │ ├── Test.cpp │ ├── Test.h │ ├── TinyXml2Helper.cpp │ ├── TinyXml2Helper.h │ ├── TrafficMonitor.cpp │ ├── TrafficMonitor.h │ ├── TrafficMonitor.rc │ ├── TrafficMonitor.vcxproj │ ├── TrafficMonitor.vcxproj.filters │ ├── TrafficMonitorDlg.cpp │ ├── TrafficMonitorDlg.h │ ├── UpdateHelper.cpp │ ├── UpdateHelper.h │ ├── WIC.cpp │ ├── WIC.h │ ├── Win11TaskbarDlg.cpp │ ├── Win11TaskbarDlg.h │ ├── Win11TaskbarSettingDlg.cpp │ ├── Win11TaskbarSettingDlg.h │ ├── WinVersionHelper.cpp │ ├── WinVersionHelper.h │ ├── WindowsSettingHelper.cpp │ ├── WindowsSettingHelper.h │ ├── WindowsWebExperienceDetector.cpp │ ├── WindowsWebExperienceDetector.h │ ├── WineTaskbarDlg.cpp │ ├── WineTaskbarDlg.h │ ├── auto_start_helper.cpp │ ├── auto_start_helper.h │ ├── crashtool.cpp │ ├── crashtool.h │ ├── language/ │ │ ├── English.ini │ │ ├── German.ini │ │ ├── Hebrew.ini │ │ ├── Hungarian.ini │ │ ├── Italian.ini │ │ ├── Polish.ini │ │ ├── Portuguese_Brazilian.ini │ │ ├── Russian.ini │ │ ├── Simplified_Chinese.ini │ │ ├── Traditional_Chinese.ini │ │ └── Turkish.ini │ ├── language.h │ ├── print_compile_time.bat │ ├── res/ │ │ ├── Acknowledgement.txt │ │ ├── Acknowledgement_en.txt │ │ └── TrafficMonitor.rc2 │ ├── resource.h │ ├── skins/ │ │ ├── 0默认皮肤/ │ │ │ └── skin.ini │ │ ├── xml_test/ │ │ │ └── skin.xml │ │ ├── 皮肤01/ │ │ │ └── skin.ini │ │ ├── 皮肤02/ │ │ │ └── skin.ini │ │ ├── 皮肤03/ │ │ │ └── skin.ini │ │ ├── 皮肤04/ │ │ │ └── skin.ini │ │ ├── 皮肤05/ │ │ │ └── skin.ini │ │ ├── 皮肤06/ │ │ │ └── skin.ini │ │ ├── 皮肤07/ │ │ │ └── skin.ini │ │ ├── 皮肤08/ │ │ │ └── skin.ini │ │ ├── 皮肤09/ │ │ │ └── skin.ini │ │ ├── 皮肤10/ │ │ │ └── skin.ini │ │ ├── 皮肤10(竖排)/ │ │ │ └── skin.ini │ │ ├── 皮肤11/ │ │ │ └── skin.ini │ │ └── 默认皮肤2/ │ │ └── skin.ini │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ └── tinyxml2/ │ ├── tinyxml2.cpp │ └── tinyxml2.h ├── TrafficMonitor.sln ├── UpdateLog/ │ ├── update_log.md │ ├── update_log_en-us.md │ └── update_log_zh-tw.md ├── include/ │ ├── OpenHardwareMonitor/ │ │ ├── OpenHardwareMonitorApi.h │ │ └── OpenHardwareMonitorGlobal.h │ └── PluginInterface.h ├── version.info ├── version_utf8.info └── 皮肤制作教程.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/BugReport_en.yaml ================================================ name: Bug Report description: File a bug report body: - type: checkboxes attributes: label: Prerequisites options: - label: I have searched for related issues in the [issues](https://github.com/halo-dev/halo/issues) list. required: true - label: I have read the [FAQ](https://github.com/zhongyang219/TrafficMonitor/blob/master/Help_en-us.md) in detail and searched for related issues in FAQ list. required: true - type: input attributes: label: Current TrafficMonitor Version description: Right-click the TrafficMonitor tray icon, select `Help` → `About`. validations: required: true - type: input attributes: label: Current Operating System Version validations: required: true - type: textarea attributes: label: What happened? description: "For ease of management, please do not report multiple unrelated issues under the same issue." validations: required: true - type: textarea attributes: label: Log Output description: "Error logs can be found in the TrafficMonitor root directory.
This will be automatically formatted as code, so no backticks are needed." render: shell - type: textarea attributes: label: Additional Information description: "If you have other information to note, you can fill it in here." ================================================ FILE: .github/ISSUE_TEMPLATE/BugReport_zh.yaml ================================================ name: Bug反馈 description: 提交Bug反馈 body: - type: checkboxes attributes: label: 前置条件 options: - label: 已经在[issue](https://github.com/zhongyang219/TrafficMonitor/issues)列表中搜索了相关问题。 required: true - label: 已详细阅读了[常见问题](https://github.com/zhongyang219/TrafficMonitor/blob/master/Help.md)并在常见问题列表中搜索了相关问题。 required: true - type: input attributes: label: 您当前使用的TrafficMonitor版本 description: 右键TrafficMonitor托盘图标,选择`帮助` → `关于`。 validations: required: true - type: input attributes: label: 您当前使用的操作系统版本 validations: required: true - type: textarea attributes: label: 发生了什么? description: "为了方便我们管理,请不要在同一个issue下报告多个不相关的问题。" validations: required: true - type: textarea attributes: label: 日志输出 description: "错误日志可在TrafficMonitor根目录中找到。
这将自动格式化为代码,因此无需反引号。" render: shell - type: textarea attributes: label: 附加信息 description: "如果你还有其他需要提供的信息,可以在这里填写。" ================================================ FILE: .github/ISSUE_TEMPLATE/FeatureRequest_en.yaml ================================================ name: Feature Request description: File a feature request body: - type: checkboxes attributes: label: Prerequisites options: - label: I have searched for related issues in the [issues](https://github.com/zhongyang219/TrafficMonitor/issues) list. required: true - label: I have read the [wiki](https://github.com/zhongyang219/TrafficMonitor/wiki/Home_en) page and make sure that the current version of TrafficMonitor does not provide this feature. required: true - type: input attributes: label: Current TrafficMonitor Version description: "Right-click the TrafficMonitor tray icon, select `Help` → `About`." validations: required: true - type: textarea attributes: label: Describe the feature description: "For ease of management, please do not report multiple unrelated issues under the same issue." validations: required: true - type: textarea attributes: label: Additional Information description: "If you have other information to note, you can fill it in here." ================================================ FILE: .github/ISSUE_TEMPLATE/FeatureRequest_zh.yaml ================================================ name: 功能请求 description: 请求一个新功能 body: - type: checkboxes attributes: label: 前置条件 options: - label: 已经在[issue](https://github.com/zhongyang219/TrafficMonitor/issues)列表中搜索了相关问题。 required: true - label: 已经阅读了[wiki](https://github.com/zhongyang219/TrafficMonitor/wiki)页面并确保当前版本TrafficMonitor没有提供此功能。 required: true - type: input attributes: label: 您当前使用的TrafficMonitor版本 description: 右键TrafficMonitor托盘图标,选择`帮助` → `关于`。 validations: required: true - type: textarea attributes: label: 描述一下该功能 description: "为了方便我们管理,请不要在同一个issue下提交多个没有相关性的特性。" validations: required: true - type: textarea attributes: label: 附加信息 description: "如果你还有其他需要提供的信息,可以在这里填写。" ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .github/workflows/main.yml ================================================ name: Release CI on: push jobs: x64_build: runs-on: windows-latest steps: - uses: actions/checkout@v3 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - name: Run msbuild run: msbuild -p:configuration=release -p:platform=x64 -p:platformToolset=v143 - name: Get current time uses: josStorer/get-current-time@v2.0.2 id: current-time with: format: YYYYMMDD_HHmmss utcOffset: "+08:00" - name : Upload artifact uses: actions/upload-artifact@v4 with: name: x64_${{ steps.current-time.outputs.formattedTime }}_TrafficMonitor path: | Bin/x64/Release/TrafficMonitor.exe Bin/x64/Release/*.dll - name : Upload pdb files uses: actions/upload-artifact@v4 with: name: x64_${{ steps.current-time.outputs.formattedTime }}_pdb path: Bin/x64/Release/*.pdb x86_build: runs-on: windows-latest steps: - uses: actions/checkout@v3 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - name: Run msbuild run: msbuild -p:configuration=release -p:platform=x86 -p:platformToolset=v143 - name: Get current time uses: josStorer/get-current-time@v2.0.2 id: current-time with: format: YYYYMMDD_HHmmss utcOffset: "+08:00" - name : Upload artifact uses: actions/upload-artifact@v4 with: name: x86_${{ steps.current-time.outputs.formattedTime }}_TrafficMonitor path: | Bin/Release/TrafficMonitor.exe Bin/Release/*.dll - name : Upload pdb files uses: actions/upload-artifact@v4 with: name: x86_${{ steps.current-time.outputs.formattedTime }}_pdb path: Bin/Release/*.pdb arm64ec_build: runs-on: windows-latest steps: - uses: actions/checkout@v3 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - name: Run msbuild run: msbuild -p:configuration=release -p:platform=ARM64EC -p:platformToolset=v143 - name: Get current time uses: josStorer/get-current-time@v2.0.2 id: current-time with: format: YYYYMMDD_HHmmss utcOffset: "+08:00" - name: Move dll file path run: mv Bin/x64/Release/*.dll Bin/ARM64EC/Release/ - name : Upload artifact uses: actions/upload-artifact@v4 with: name: arm64EC_${{ steps.current-time.outputs.formattedTime }}_TrafficMonitor path: | Bin/ARM64EC/Release/TrafficMonitor.exe Bin/ARM64EC/Release/*.dll - name : Upload pdb files uses: actions/upload-artifact@v4 with: name: arm64EC_${{ steps.current-time.outputs.formattedTime }}_pdb path: Bin/ARM64EC/Release/*.pdb # winXP_build: # runs-on: windows-latest # steps: # - uses: actions/checkout@v3 # - name: Add msbuild to PATH # uses: microsoft/setup-msbuild@v1.1.3 # - name: Run msbuild # run: | # set ExternalCompilerOptions=/DCOMPILE_FOR_WINXP # msbuild -p:configuration=release -p:platform=x86 -p:platformToolset=v140_xp # shell: cmd # - name: Get current time # uses: josStorer/get-current-time@v2.0.2 # id: current-time # with: # format: YYYYMMDD_HHmmss # utcOffset: "+08:00" # - name : Upload artifact # uses: actions/upload-artifact@v3 # with: # name: winXP_${{ steps.current-time.outputs.formattedTime }}_TrafficMonitor # path: Bin/Release/TrafficMonitor.exe ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Typescript v1 declaration files typings/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs *.ini !skin.ini *.dat *.bak /TrafficMonitor/skins/test* [Dd]ebug (without temperature)/ [Rr]elease (without temperature)/ [Dd]ebug (lite)/ [Rr]elease (lite)/ /TrafficMonitor/compile_time.txt !/TrafficMonitor/language/*.ini *.diagsession ================================================ FILE: Help.md ================================================ **简体中文 | [English](./Help_en-us.md)** ## TrafficMonitor 常见问题 这里是关于TrafficMonitor常见问题的页面,如果你想查看关于TrafficMonitor的各项功能和使用方法的详细介绍,请[点击这里](https://github.com/zhongyang219/TrafficMonitor/wiki)转到TraffinMonitor Wiki页面。 ### Windows11下任务栏窗口和系统小组件重叠 在Windows11下,可以通过以下步骤解决此问题: 打开“选项设置”——“任务栏设置”,点击“Windows11相关设置”按钮,勾选“避免与右侧小组件重叠”选项,如下图所示。 image-20250310121048889 如果你的任务栏中没有显示“小组件”,则此选项是灰色不可用状态。 ### Windows11下“任务栏窗口显示在任务栏左侧”是灰色不可用状态 在Windows11,只有在任务栏居中的情况下,此选项才可用。 因为当任务栏靠左显示时,左侧没有给TrafficMonitor显示的空间。 ### Windows11下,当任务栏被占满时,TrafficMonitor的任务栏窗口和任务栏图标重叠 image-20250310173257156 这个问题目前无法解决,建议尽量不要让任务栏被占满,或者在系统设置中将“合并任务栏按钮和标签”改为“始终”。 请不要向我反馈此问题。 ### 如何显示CPU和内存利用率? 在主窗口点击右弹出键菜单,勾选“显示更多信息”。如果需要在任务栏窗口中也显示CPU和内存利用率,则在任务栏窗口中点击右键弹出菜单,选择“显示设置”,在弹出对话框中勾选“CPU和内存利用率”即可。 ### 如何更改任务栏中“CPU”、“内存”等文本 任务栏窗口中的标签文本是可以自定义的。点击鼠标右键,在右键菜单中选择“选项”,点击“任务栏窗口设置”,点击“显示文本设置”,双击项目右侧的文本即可更改,然后点击两次“确定”即可。 image-20221119093547649 由于这些文本是可以自定义的,因此切换语言时不会自动更改。在切换语言后,你可以点击“显示文本”对话框中的“恢复默认”按钮。 ### 网速一直显示为0KB 这种情况可能是因为你电脑中正在使用的网卡发生了切换导致的。 点击右键菜单“选择网络连接”下面的“刷新网络连接列表”也许可以解决这个问题。 image-20221119094324759 如果问题仍然存在,请尝试在“选择网络连接”的子菜单下选择你要监控的网络连接,而不是选择“自动选择”。 如果问题仍然存在,请尝试在“选项设置”>“常规设置”>“高级”中点击“选择要监控的网络连接”按钮,在弹出的对话框中只勾选你想监控的网络连接,去掉其他项目的勾选,点击两次“确定”按钮即可。 image-20221119094501312 ### 如何单独设置任务栏窗口中每个项目的颜色? 在右键菜单中选择“选项”,切换到“任务栏窗口设置”,勾选“指定每个项目的颜色”,此时再点击“文本颜色”右边的颜色块,就会弹出“任务栏窗口颜色设置”的对话框了。 如果不勾选“指定每个项目的颜色”,则只能为文本设置统一的颜色。 ### 设置了开机自动运行仍然无法开机自启。 从1.80版本开始,标准版和Lite版采用了不同的方式来实现开机自启动。 * Lite版和1.80以前的版本: Lite版和1.80以前的版本的开机启动功能是通过在注册表“计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run”中创建“TrafficMonitor”的注册表项来实现的,如果你遇到无法开机启动的问题,请先检查该注册表项是否存在,再检查程序的路径是否正确。如果你移动了程序的位置,则会因为路径无效导致开机自启动失效,此时只需要在选项设置中取消“开机自动运行”的勾选,再将其勾选上就可以了。 注意,某些第三方安全软件可能会阻止TrafficMonitor的开机自启动,请尝试在安全软件中允许TrafficMonitor开机自启动。 如果设置了以管理员身份运行也会出现开机无法自启动的问题,请尝试去掉以管理员身份运行。 * 标准版: 标准版是通过创建任务计划来实现开机自启动的。可以通过`控制面板\系统和安全\管理工具`来打开任务计划程序。 如下图所示: 如果你遇到无法开机自启动的情况,请到“任务计划程序”中检查TrafficMonitor的计划任务是否正常创建,exe文件的路径是否正确。 开机无法自启动的一个常见原因是你可能移动了TrafficMonitor主程序的位置。如果你为TrafficMonitor设置好了开机自动运行,但是你将TrafficMonitor移动到了其他位置,那么开机自启动肯定就失效了。你需要打开TrafficMonitor的“选项设置”——“常规设置”,如果“开机时自动运行”处于勾选状态,先去掉勾选,然后再次打开“选项设置”——“常规设置”,重新勾选“开机时自动运行”即可。 需要注意的是,如果你使用不含温度监控的版本在注册表中创建了开机自启动项,然后再使用包含温度监控的版本开启开机自启动功能,它会自动将注册表中的开机自启动项删除,再在任务计划中创建开机自启动项。反之亦然。 ### 程序弹出“无法保存设置”的对话框。 如果遇到这种情况,说明程序没有向其所在目录下写入数据的权限,导致设置数据无法保存。尝试将程序移动到其他有写入权限的文件夹中可以解决这个问题。 你也可以通过以下步骤将配置文件的保存路径改为C:\Users\\<用户名\>\AppData\Roaming\TrafficMonitor目录。 * 退出TrafficMonitor,以管理员身份重新启动TrafficMonitor。 * 在右键菜单中选择“选项”,切换到“常规设置”选项卡,在“数据和配置文件”中选择“保存到Appdata目录”。 如果此时仍然提示“无法保存设置”,请打开应用程序所在目录,打开`global_cfg.ini`文件,如果不存在请新建一个,在里面添加如下内容: ``` [config] portable_mode = false ``` 如果无法新建,可以在其他位置(比如桌面)新建该文件,然后移动到程序所在目录。 如果`global_cfg.ini`文件已存在,就把`portable_mode `的值改成`false`,保存后重新启动TrafficMonitor。 如果`global_cfg.ini`没有写入权限,可以尝试把该文件复制到桌面,改好后复制回原来的路径将原文件覆盖。 执行以上步骤后理论上应该不会出现这种问题了。如果这个问题仍然出现,请尝试把C:\Users\\<用户名\>\AppData\Roaming\TrafficMonitor\config.ini删除,该文件会在删除后重新生成。 在1.79以后的版本中,如果程序所在目录无法写入数据,会自动将配置和数据文件保存到C:\Users\\<用户名\>\AppData\Roaming\TrafficMonitor目录,此时,“选项”——“常规设置”——“数据和配置文件”中“保存到程序所在目录”将不可用。 ### 更改设置后下次开机时设置丢失 如果你没有看到“无法保存设置”的对话框,则说明程序的配置文件是能正常保存的。因此这个问题的原因可能是你的电脑中存在了多个TrafficMonitor的程序文件,并且配置文件保存在了程序所在目录下。 例如,你打开了“D:\software\TrafficMonitor\TrafficMonitor.exe”,并且更改了选项设置,但是下次开机时启动的却是“D:\software1\TrafficMonitor\TrafficMonitor.exe”,而你之前修改的配置被保存在了“D:\software\TrafficMonitor\”目录下。 解决这个问题的方法是: 1. 在“选项设置”>“常规设置”>“配置和数据文件”中,将配置文件的保存位置更改为Appdata目录。 2. 删除你电脑中不需要的TrafficMonitor的程序文件,启动TrafficMonitor后,打开“选项设置”>“常规设置”,点击“重新设置开机自动运行”按钮。 ### 网速数值显示不全。 由于不同字体每个字符的宽度并不一样,在某些情况下,确实会出现网速数值显示不全的问题。如果出现了这样的问题,请打开“选项”——“任务栏窗口设置”,在“数据位数”下拉列表中选择一个更大的值。 ### 设置了鼠标穿透后如何取消? 在通知区的TrafficMonitor的图标上点击鼠标右键,去掉“鼠标穿透”的勾选即可。 设置了鼠标穿透后,悬浮窗将无法响应任何鼠标消息,也无法弹出右键菜单,但是可以通过通知区图标来弹出右键菜单。主窗口的右键菜单和通知区图标的右键是完全一样的。 另外,即使你之前设置了隐藏通知区图标,开启鼠标穿透后,通知区图标也会自动显示出来,防止无法弹出右键菜单。 说明:以下几种情况下通知区图标会自动显示出来: * 开启鼠标穿透后; * 不显示任务栏窗口的情况下隐藏主窗口后; * 隐藏主窗口的情况下关闭任务栏窗口后; * 开启鼠标穿透的情况下关闭任务栏窗口后。 ### Windows 10 白色任务栏主题时任务栏窗口颜色的问题 在使用白色任务栏主题时,你可以在在“任务栏窗口设置”点击“预设方案”按钮,选择“浅色模式”,可以一键设置浅色模式任务栏颜色。如图所示: 同时,你还可能勾选“自动适应Windows10深色/浅色主题”,程序会在Windows10深色/浅色主题更换时自动切换颜色方案。你可以点击“自动适应设置”按钮来配置在深色和浅色主题时分别使用哪个颜色预设。 ### 在Windows7/Windows8/8.1下任务栏窗口有个背景色,无法完全透明 这个问题确实存在,但是在Win10下是正常的。这个问题暂时无法解决。 在1.79以后的版本中,Windows8/8.1下可以在“选项”——“任务栏窗口设置”中勾选“背景色透明”,再勾选“根据任务栏颜色自动设置背景色”即可获得较好的显示效果。 ### 任务栏窗口有时会显示不全,比如单位被覆盖了 这确实是一个BUG,但是我目前还没有找到一个好的解决方法,这个问题通常出现在任务栏右侧通知区域宽度变化的时候,主要在在切换输入法的时候,如果出现了这个问题,可以将通知区任意一个图标向上拖动将其隐藏,再将其拖下来即可恢复正常。 出现这个问题原因在于,由于系统任务栏通知区的图标数量可能会发生变化,导致通知区的宽度也会时常变化,当通知区的宽度发生变化时,TrafficMonitor的任务栏窗口需要实时调整其位置。但是由于我无法知道通知区的宽度在什么时候变化,因此只能每隔一段时间判断是否需要调整位置,如果任务栏通知区域的宽度变化得太快,就会导致TrafficMonitor的任务栏无法及时调整其位置,从而导致了这个BUG。 **以下步骤或许可以解决这个问题:** * 打开“设置” * 点击“时间和语言”——“区域和语言” * 点击右侧“高级键盘设置” * 勾选“使用桌面语言栏” * 右键点击任务栏,选择“任务栏设置” * 点击“打开或关闭系统图标”,关闭“输入指示” 方法来自知乎 [win10的任务栏为何一点击就乱动?](https://www.zhihu.com/question/312032145/answer/627965084) ### Windows10中开启HDR后任务栏窗口无法显示 部分用户反馈,在Windows10中开始HDR功能会导致任务栏窗口无法显示。如果遇到这个问题,可以尝试在[“选项设置”——“任务栏窗口设置”](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#任务栏窗口设置)中关闭“背景透明”选项的勾选。 在1.85版本以后,可以通过在“选项设置”——“任务栏窗口设置”——“渲染设置”中改成Direct2D渲染来解决这个问题。 ### CPU利用率显示和任务管理器不一致 在Windows10及以上操作系统中,如果你需要让TrafficMonitor显示的CPU利用率和任务管理器一致,请到[“选项设置”——“常规设置”——“高级”](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#高级)——“CPU使用率获取方式”中选择“使用性能计数器”。 由于Windows10以上操作系统中任务管理器获取CPU利用率的方式发生了改变,因此选择“基于CPU使用时间”的方式获取到的CPU利用率会和任务管理器中显示的不一致。 ### 关于TrafficMonitor温度监控的问题 由于温度监控功能在某些电脑中存在一些问题,因此温度监控功能默认是关闭的,如果你要使用TrafficMonitor的温度监控功能,请到[“选项设置”-“常规设置”-“硬件监控”](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#硬件监控)中开启。开启后,任务栏右键菜单中的“显示设置”子菜单下才会出现温度相关的项目。 TrafficMonitor的温度监控功能依赖第三方开源库[LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor)。如果你遇到硬件温度无法显示,或者显示的温度异常的问题,请先下载LibreHardwareMonitor,并查看LibreHardwareMonitor是否能正常显示对应的温度。如果下载的最新版LibreHardwareMonitor可以正常监控到硬件温度,则将下载的LibreHardwareMonitor文件夹中的LibreHardwareMonitorLib.dll替换掉TrafficMonitor文件夹下的LibreHardwareMonitorLib.dll文件,即可解决温度无法检测的问题。 请不要向我反馈诸如温度显示不准确、显卡利用率不准确之类的问题,此类问题我无法解决。TrafficMonitor不是专业的硬件监控软件,无法保证硬件监控的数据在所有设备上的准确性。 **注意:硬件监控功能(包括温度监控和显卡使用率监控)可能存在一些问题,它可能会占用更多的CPU和内存。据部分用户反馈,开启温度功能后会导致程序崩溃和系统死机等问题,请在知晓以上风险后再决定开启硬件监控功能。否则,请不要使用硬件监控功能。** ### 程序启动时提示找不到“MSVC\*.dll”、“mfc\*.dll”、“vc*.dll” 点击以下链接下载并安装Microsoft Visual C++ 运行环境。 [最新支持的 Visual C++ 可再发行程序包下载 | Microsoft Docs](https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist?view=msvc-170) ### 我希望在任务栏上显示更多硬件信息,比如风扇转速、功率等 现在推出了硬件监控插件,可以显示LibreHardwareMonitor中所有监控项目。 ![](Screenshots/420555677-53bd3ac9-1c55-4212-aa68-1fe5711e9fbc.png) 使用说明和下载链接请点下方链接转到插件下载页面: [TrafficMonitorPlugins/download/plugin_download.md at main · zhongyang219/TrafficMonitorPlugins](https://github.com/zhongyang219/TrafficMonitorPlugins/blob/main/download/plugin_download.md) ### 关于程序崩溃问题 如果遇到程序崩溃的问题,重新启动程序后,请先到“选项”——“常规设置”——“硬件监控”中关闭所有项目的硬件监控,因为基于用户提供的dmp文件发现,大多数崩溃问题都和硬件监控功能有关。和硬件监控功能相关的崩溃问题我无法解决,请不要向我发送邮件反馈。如果你排除了硬件监控的问题,但是崩溃问题仍然存在,请先到“选项”——“常规设置”——“高级”——“插件管理”中禁用所有插件,再重新启动程序(如果程序无法启动,请删除TrafficMonitor所在目录下plugins里面的所有dll文件)。如果排除了插件的问题,但是崩溃问题仍然存在,请根据崩溃弹窗中的提示向我发送电子邮件。 如果还遇到其他问题,也可以点击“关于”对话框中的“联系作者”,或者直接[点击此处](mailto:zhongyang219@hotmail.com)向我发送电子邮件。但我由于作者的能力有限,我并不能保证可以解决所有问题,但是你的反馈也许可以帮助我更好的改进这个软件。 请在邮件中尽可能详细地描述你遇到的问题,出现了什么错误提示,你尝试过哪些操作等,最好能够附上截图和配置文件(“选项”——“常规设置”——“打开配置文件所在路径”)。 注意,发送前请先确认一下你发送时使用的电子邮件地址,如果你的邮件地址是形如“outlook_随机字符串@outlook.com”的格式,那么这样的邮箱地址是无法回复的。 这可能是由于你使用了第三方邮箱地址作为Microsoft账号登录Windows导致的。如果有这样的情况,请务必在邮件中附上正确的电子邮件地址。 ================================================ FILE: Help_en-us.md ================================================ **[简体中文](./Help.md) | English** # TrafficMonitor Frequently Asked Questions This is the page about the Frequently Asked Questions of TrafficMonitor. If you want to view the detailed introduction of the functions and usage of TrafficMonitor, please [click here](https://github.com/zhongyang219/TrafficMonitor/wiki) to go the the TraffinMonitor Wiki page. ### In Windows 11, the taskbar window overlapped with the windows Widgets In Windows 11, this issue can be fixed with the following steps: Open "Option Settings"-"Taskbar Window Settings", click the "Settings related to Windows 11" button, and check "Avoid overlapping with right Widgets" option. As shown in the picture bellow. image-20250310121048889 If the Widgets is not showing on the taskbar, this option is grayed. ### In Windows 11, "The taskbar window appears to the left of the taskbar" is grayed In Windows 11, this option is only available when the taskbar is align centered. Because when the taskbar is displayed on the left, there is no space on the left side for TrafficMonitor to display. ### In Windows 11, the taskbar window of TrafficMonitor overlaps with the taskbar icons when taskbar is full image-20250310173257156 This problem cannot be fixed at present. It is recommended to try not to fill the taskbar, or change "Merge taskbar buttons and tabs" to "Always" in the system settings. Please do not send feedback to me on this issue. ### How to show the CPU and memory usage? Right click the main window and check "Show More Info". If you also need to display the CPU and memory usage in the taskbar window, right click the taskbar window and select "Display Settings", check "CPU and Memory Usage" in the pop-up dialog box. ### How do I change the text such as "CPU" and "MEM" in the taskbar The label text in the taskbar window can be customized. Right click on the taskbar window, select "Options", click "Taskbar Windows Settings", click "Display Text settings" button, the text on the right side of the item can be modified by double click. After modified, click "OK" button twice. image-20221119093547649 Because the text is customizable, it does not automatically change when you switch the language. After you swatch the language, you can click "Restore default" button in the "Display Text settings" dialog box. ### The net speed is always displayed as 0KB This may caused by the switch of the network you are using on your computer. Click "Refresh connection list" under "Select network connections" in the context menu may solve this problem. image-20221119094324759 If this problem still exists, please try to select the network connection you want to monitored in the "Select network connections" submenu instead of select "Auto select". If this problem still exists, please try to click the "Select the connection to monitor" button in "Options Settings" > "General Settings" > "Advanced", check the network you want to monitored, uncheck other items, and click "OK" button twice. image-20221119094501312 ### How do I set the color of each item in the taskbar window individually? Select "Options" In the right-click menu, switch to "Taskbar Window Settings" tab, check "Specify colors for each item", and then click the color block on the right side of "Text Color " to pop up the dialog box for taskbar window color settings. If you do not check "Specify colors for each item", you can only set the uniform color for the text. ### "Auto run when Windows start" dose not work Starting from version 1.80, the version with temperature monitoring and the version without temperature monitoring have adopted different methods to realize "auto run when Windows start". * Versions without temperature monitoring and versions before 1.80: The auto run function of the versions without temperature monitoring and versions before 1.80 is achieved by creating the "TrafficMonitor" key in the registry path of "Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run". If you encounter the problem of auto run dose not work, please check that the registry exists, and then check the program path is correct. If you move the location of the program, the auto run will be invalid because of the program location being invalid. In this case, you only need to uncheck "Auto run when Windows start" in the option settings, and then check it on. Note that some third-party security software may prevent TrafficMonitor from booting up automatically. Please try to allow TrafficMonitor to boot up automatically in the security software. If you set the program to run as an administrator, the auto run function will also not work. Please try to remove running as an administrator. * Version with temperature monitoring: The version that includes temperature monitoring implements auto run function by creating a task plan. The task scheduler can be opened through `Control Panel\System and Security\Administrative Tools`. As shown below: If you encounter that situation that the program cannot be started automatically after booting, please go to "Task Scheduler" to check whether the scheduled task of TrafficMonitor is created normally and the path of the exe file is correct. A common reason why the "Auto run when Windows start" dose not work is that you may have moved the location of the TrafficMonitor main program. If you have set up TrafficMonitor to run automatically at startup, but you move TrafficMonitor to another location, then the auto run at startup will definitely fail. You need to open "Option Settings"-"General Settings" of TrafficMonitor, if "Auto run when Windows start" is checked, remove the check, then open "Option Settings"-"General Settings" again, and check again "Auto run when Windows start" will be fine. It should be noted that if you use the version without temperature monitoring to create a auto-start item in the registry, and then use the version that with temperature monitoring to turn on the "Auto run when Windows start" function, it will automatically delete the auto-start item in the registry, and then create a auto-start item in the task plan. vice versa. ### The program pops up the "Unable to Save Settings" message box. If you encountered this problem, that means the program does not have permission to write data to its directory, causing the settings data cannot be saved. Try to move the program to another folder that has write permissions will save this problem. You can also change the save path of the configuration and data file to the C:\Users\\\AppData\Roaming\TrafficMonitor directory by the following steps. * Exit TrafficMonitor and restart TrafficMonitor as an administrator. * Select "Options" in the right-click menu, switch to the "General Settings" tab, and select "Save to AppData Directory" in "Configuration and data files". If it still pops up the "Unable to Save Settings" message box, open the directory where the application is located, open the `global_cfg.ini` file. If it doesn't exist, create a new one, adding the following: ``` [config] portable_mode = false ``` If the file cannot be created, you can create the file in a different location, such as the desktop, and then move to the directory where the program is located. If the `global_cfg.ini` file already exists, change the value of `portable_mode` to `false` and save, then restart TrafficMonitor. If `global_cfg.ini` does not have write permission, you can try copying the file to the desktop, and then copying it back to the original path to overwrite the original file after modified. After these steps, this problem should not theoretically occur. If this problem still occurs, try to delete the file C:\Users\\\AppData\Roaming\TrafficMonitor\config.ini. This file will be regenerated after it is deleted. In version 1.79 and later, if the directory where the program located is not writable, the configuration and data files will be automatically saved to the "C:\Users\\\AppData\Roaming\TrafficMonitor" directory. At this time, "Options" -“General Settings”-“Save to the program directory” in “Configuration and data files” will not be available. ### The configuration is lost after next start up If you do not see the "Unable to Save Settings" message box, that means the configuration file can be saved normally. The cause of this problem may be that you have multiple TrafficMonitor application files on your computer and the configuration files are saved in the application directory. For example, you have open "D:\software\TrafficMonitor\TrafficMonitor.exe", and modified the configuration, but next time you start your computer, "D:\software1\TrafficMonitor\TrafficMonitor.exe" is started, while the configuration files you modified before is saved in "D:\software\TrafficMonitor\\". The method to solve this problem is: 1. Change the save location of the configuration files to AppData directory in "Option Settings" > "General Settings" > "Configuration and data file". 2. Delete the TrafficMonitor application files you don't need on your computer, start TrafficMonitor, open "Option Settings" > "General Settings", click "Reset autorun" button. ### The net speed value is not fully displayed. Because the width of each character in different fonts is not the same, in some cases, it does appear the problem of the net speed value is not fully displayed. If this problem occurs, open "Options"-"Taskbar Window Settings", and select a larger value in the "Number of digits" drop down list. ### How to cancel after setting the mouse penetrate? Right click the TrafficMonitor icon in the notification area, uncheck the "Mouse Penetrate". After the mouse penetrate is set, the suspension window will not be able to respond to any mouse messages, or pop-up right-click menu. But the menu can be popped up by right click the icon in notification area. The right-click menu of the main window and which of the notification area icon is exactly the same. In addition, even if you have previously set the hidden notify icon, when the "mouse penetrate" is set, the notify icon will also automatically appear to prevent the right-click menu cannot be displayed. Note: The notification area icon will be automatically displayed in the following situations: * After the "Mouse Penetrate" is set; * Hide the main window without displaying the taskbar window; * Close the taskbar window when the main window is hidden; * Close the taskbar window when the "Mouse Penetrate" is set. ### Problems with the taskbar window color in Windows 10 white taskbar theme When using the white taskbar theme, you can click the "Preset" button in the "Taskbar Window Settings" and select "Light Mode" to set the taskbar color in light mode. As the picture shows: At the same time, you may also check "Auto adapt to Windows 10 dark/light theme", the program will automatically switch the color preset when Windows 10 dark/light theme is changed. You can click the "Auto Adapt settings" button to configure which color preset to use for dark and light themes. ### The taskbar windows cannot be displayed when the HDR is turned on in Windows 10 Some users have reported that turning on the HDR function in Windows 10 will cause the taskbar window to fail to display. If you encounter this problem, you can try turning off the "Background Transparent" option in ["Option Settings" - "Taskbar Window Settings"](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#任务栏窗口设置). Since version 1.85, this can be fixed by changing to Direct2D rendering in "Options Settings" - "Taskbar Window Settings" - "Rendering Settings". ### The CPU usage displayed is inconsistent with Task Manager Since Windows 10, if you want to make the CPU usage shown by TrafficMonitor consistent with Task Manager, please go to "Option Settings"-"General Settings"-"Advanced"-"CPU usage acquisition method", and select "Use the performance counter". ### About the temperature monitoring of TrafficMonitor Due to some problems occurred in some computers caused by the temperature monitoring function, the temperature monitoring function is turned off by default. If you want to use the temperature monitoring function of TrafficMonitor, please go to ["Option Settings"-"General Settings"-"Hardware Monitoring"](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#硬件监控) to enable it. After it is turned on, temperature-related items will appear under the "Display Settings" submenu in the taskbar right-click menu. The temperature monitoring function of TrafficMonitor relies on a third-party open source library [LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor). If you encounter the problem that the hardware temperature cannot be displayed, or the displayed temperature is incorrect, then please download LibreHardwareMonitor first, and check whether LibreHardwareMonitor can display the corresponding temperature normally. If LibreHardwareMonitor cannot display the temperature of the corresponding hardware, then I cannot solve this problem. You can report your problem to the author of LibreHardwareMonitor. If the latest version of LibreHardwareMonitor can display the hardware temperature normally, replace the LibreHardwareMonitorLib.dll files in the TrafficMonitor folder with the downloaded the LibreHardwareMonitorLib.dll files may solve this problem. **Note: The hardware monitoring function (including temperature monitoring and GPU usage monitoring) may have some problems, which may cause more CPU and memory usage. According to feedback from some users, turning on the temperature function will cause problems such as program crashes and system crashes. Please decide to turn on the hardware monitoring function after you are aware of the above risks. Otherwise, please do not use the hardware monitoring function.** ## The program prompts that "MSVC\*.dll" or "mfc\*.dll" cannot be found when the program starts Click the link below to download and install the Microsoft Visual C++ runtime environment. [Latest supported Visual C++ Redistributable downloads | Microsoft Docs](https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) ### I want to have more hardware information on the taskbar, such an fan speed, power, etc The Hardware Monitor plug in is now available. It can display all the monitoring items in LibreHardwareMonitor. ![](images/420555677-53bd3ac9-1c55-4212-aa68-1fe5711e9fbc.png) For instructions and download links, please click the link below to go to the plug-in download page: [TrafficMonitorPlugins/download/plugin_download.md at main · zhongyang219/TrafficMonitorPlugins](https://github.com/zhongyang219/TrafficMonitorPlugins/blob/main/download/plugin_download.md) ### About the crash issue If you encounter the problem of program crash, please turn off all the items in "Options" - "General Settings" - "Hardware Monitoring" after restarting the program, because based on the DMP file provided by the user, most crash problems are related to the hardware monitoring function. I cannot solve the crash problem related to the hardware monitoring function, please do not send me email feedback. If you have excluded the hardware monitoring problem, but the crash problem still exists, please go to "Options" - "General Settings" - "Advanced" - "Plug-in Management" to disable all plugins, and then restart the program. (If the program fails to start, please delete all the `dll` files in the `plugins` in the directory where TrafficMonitor is located). If you have excluded the plugin problem, but the crash problem still exists, please send me the email according to the prompts in the crash window. If you have encountered any other problems, you can also click "Contact Author" in the "About" dialog box, or directly [click here](mailto:zhongyang219@hotmail.com) to send me an email. Please describe the problems you have encountered in detail, the error prompts, what operations you have tried, it is better to attach the screenshots and configuration files ("Options"-"General Settings"-"Open configuration file path"). Note: Please confirm the email address that you used before sending the email. If your email address is form like "Outlook_\@outlook.com", then such email address is unable to reply. This may be due to the fact that you are using a third-party e-mail address as the Microsoft account to log on to Windows. If this is the case, be sure to enclose the correct email address in the message. ================================================ FILE: LICENSE ================================================ Copyright (c) <2019> "Anti 996" License Version 1.0 (Draft) Permission is hereby granted to any individual or legal entity obtaining a copy of this licensed work (including the source code, documentation and/or related items, hereinafter collectively referred to as the "licensed work"), free of charge, to deal with the licensed work for any purpose, including without limitation, the rights to use, reproduce, modify, prepare derivative works of, distribute, publish and sublicense the licensed work, subject to the following conditions: 1. The individual or the legal entity must conspicuously display, without modification, this License and the notice on each redistributed or derivative copy of the Licensed Work. 2. The individual or the legal entity must strictly comply with all applicable laws, regulations, rules and standards of the jurisdiction relating to labor and employment where the individual is physically located or where the individual was born or naturalized; or where the legal entity is registered or is operating (whichever is stricter). In case that the jurisdiction has no such laws, regulations, rules and standards or its laws, regulations, rules and standards are unenforceable, the individual or the legal entity are required to comply with Core International Labor Standards. 3. The individual or the legal entity shall not induce, suggest or force its employee(s), whether full-time or part-time, or its independent contractor(s), in any methods, to agree in oral or written form, to directly or indirectly restrict, weaken or relinquish his or her rights or remedies under such laws, regulations, rules and standards relating to labor and employment as mentioned above, no matter whether such written or oral agreements are enforceable under the laws of the said jurisdiction, nor shall such individual or the legal entity limit, in any methods, the rights of its employee(s) or independent contractor(s) from reporting or complaining to the copyright holder or relevant authorities monitoring the compliance of the license about its violation(s) of the said license. THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. ================================================ FILE: LICENSE_CN ================================================ 版权所有(c)<2019> 反996许可证版本1.0 在符合下列条件的情况下,特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以 下统称为“授权作品”)的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括 但不限于使用、复制,修改,衍生利用、散布,发布和再许可: 1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不得自行修 改。 2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或经营地( 以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和标准。如果该司法管辖区 没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可执行,则个人或法人实体必须遵守国际 劳工标准的核心公约。 3. 个人或法人不得以任何方式诱导、暗示或强迫其全职或兼职员工或其独立承包人以口头或书面形式同意 直接或间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和标准保护的权利 或补救措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该等个人或法人实体也不得以任 何方法限制其雇员或独立承包人向版权持有人或监督许可证合规情况的有关当局报告或投诉上述违反许可证 的行为的权利。 该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵 权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均不承担因本软件或 本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。 ================================================ FILE: OpenHardwareMonitorApi/LibreHardwareMonitorLib.xml ================================================ LibreHardwareMonitorLib Stores all hardware groups and decides which devices should be enabled and updated. Creates a new instance with basic initial . Creates a new instance with additional . Computer settings that will be transferred to each . Contains computer information table read in accordance with System Management BIOS (SMBIOS) Reference Specification. Triggers the method for the given observer. Observer who call to devices. Triggers the method with the given visitor for each device in each group. Observer who call to devices. If hasn't been opened before, opens , , and triggers the private method depending on which categories are enabled. If opened before, removes all and triggers , and . If opened before, removes all and recreates it. specific additional settings passed to its . Support for the Kraken X3 devices from NZXT Initializes a new instance of the class. The group. The thread. The affinity. Gets the specified . The group. The thread. . Gets the CPUID. Gets the CPU index. Sets the default fan speed. Gets the OverdriveN temperature. The type. The sensor. The minimum temperature. The scale. If set to true, resets the sensor value to null. Gets a PMLog sensor value. The data. Type of the sensor. The sensor. The factor. Gets the Overdrive6 power. The type. The sensor. Initializes a new instance of the class. Component name. Identifier that will be assigned to the device. Based on Additional settings passed by the . Gets the device identifier. This structure describes a group-specific affinity. Initializes a new instance of the struct. The group. The mask. Gets a single group affinity. The group. The index. . Gets the group. Gets the mask. Determines whether the specified is equal to this instance. The to compare with this instance. true if the specified is equal to this instance; otherwise, false. Returns a hash code for this instance. A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. Implements the == operator. The a1. The a2. The result of the operator. Implements the != operator. The a1. The a2. The result of the operator. Object representing a component of the computer. Individual information can be read from the . Creates a new instance based on the data provided. Component name. Identifier that will be assigned to the device. Based on Additional settings passed by the . Event triggered when is closing. Collection of identifiers representing the purpose of the hardware. Handler that will trigger the actions assigned to it when the event occurs. Component returned to the assigned action(s). Basic abstract with methods for the class which can store all hardware and decides which devices are to be checked and updated. Triggered when a new is registered. Triggered when a is removed. Gets a list of all known . Can be updated by . List of all enabled devices. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about: devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about or devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Gets or sets a value indicating whether collecting information about devices should be enabled and updated. if a given category of devices is already enabled. Generates full LibreHardwareMonitor report for devices that have been enabled. A formatted text string with library, OS and hardware information. Represents a unique / identifier in text format with a / separator. Creates a new identifier instance based on the base and additional elements. Base identifier being the beginning of the new one. Additional parts by which the base will be extended. Abstract parent with logic for the abstract class that stores data. Accepts the observer for this instance. Computer observer making the calls. Call the method for all child instances (called only from visitors). Computer observer making the calls. A group of devices from one category in one list. Gets a list that stores information about in a given group. Report containing most of the known information about all in this . A formatted text string with hardware information. Stop updating this group in the future. Handler that will trigger the actions assigned to it when the event occurs. Component returned to the assigned action(s). Abstract object that stores information about a device. All sensors are available as an array of . Can contain . Type specified in . Gets a unique hardware ID that represents its location. Gets or sets device name. Gets the device that is the parent of the current hardware. For example, the motherboard is the parent of SuperIO. Gets an array of all sensors such as , , etc. Gets child devices, e.g. of the . Report containing most of the known information about the current device. A formatted text string with hardware information. Refreshes the information stored in array. An that will be triggered when a new sensor appears. An that will be triggered when one of the sensors is removed. Gets rarely changed hardware properties that can't be represented as sensors. Abstract object that represents additional parameters included in . Gets a parameter default value defined by library. Gets a parameter description defined by library. Gets a unique parameter ID that represents its location. Gets or sets information whether the given is the default for . Gets a parameter name defined by library. Gets the sensor that is the data container for the given parameter. Gets or sets the current value. Category of what type the selected sensor is. Stores the readed value and the time in which it was recorded. of the sensor. The time code during which the was recorded. Gets the value of the sensor Gets the time code during which the was recorded. Stores information about the readed values and the time in which they were collected. Gets the unique identifier of this sensor for a given . Gets a maximum value recorded for the given sensor. Gets a minimum value recorded for the given sensor. Gets or sets a sensor name. By default determined by the library. Gets the last recorded value for the given sensor. Gets a list of recorded values for the given sensor. Resets a value stored in . Resets a value stored in . Abstract object that stores information about the limits of . Upper limit of value. Lower limit of value. Abstract object that stores information about the critical limits of . Critical upper limit of value. Critical lower limit of value. Abstract object that stores settings passed to , and . Returns information whether the given collection of settings contains a value assigned to the given key. Key to which the setting value is assigned. Assigns a setting option to a given key. Key to which the setting value is assigned. Text setting value. Gets a setting option assigned to the given key. Key to which the setting value is assigned. Default value. Removes a setting with the specified key from the settings collection. Key to which the setting value is assigned. Base interface for creating observers who call to devices. Refreshes the values of all in all on selected . Instance of the computer to be revisited. Refreshes the values of all on selected . Instance of the hardware to be revisited. Refreshes the values on selected . Instance of the sensor to be revisited. Refreshes the values on selected . Instance of the parameter to be revisited. Chipset temperature [℃] CPU temperature [℃] motherboard temperature [℃] "T_Sensor" temperature sensor reading [℃] VRM temperature [℃] CPU Core voltage [mV] CPU_Opt fan [RPM] VRM heat sink fan [RPM] Chipset fan [RPM] Water Pump [RPM] Water flow sensor reading [RPM] CPU current [A] "Water_In" temperature sensor reading [℃] "Water_Out" temperature sensor reading [℃] Water block temperature sensor reading [℃] An unsafe but universal implementation for the ACPI Embedded Controller IO interface for Windows It is unsafe because of possible race condition between this application and the PC firmware when writing to the EC registers. For a safe approach ACPI/WMI methods have to be used, but those are different for each motherboard model. Selects another bank. Memory from 0x10-0xAF swaps to data from new bank. Beware to select the default bank 0 after changing. Bank selection is reset after power cycle. New bank index. Can be a value of 0-3. Represents the motherboard of a computer with its and as . Creates motherboard instance by retrieving information from and creates a new based on data from and . table containing motherboard data. Additional settings passed by . Gets the name obtained from . Always Gets the information. Motherboard itself cannot be updated. Update instead. Closes using . Composite class containing information about the selected . Creates a new instance and assigns values. Name of the selected component. Description of the selected component. Default value of the selected component. Gets a name of the parent . Gets a description of the parent . Gets a default value of the parent . Observer making calls to selected component 's. Creates a new observer instance. Instance of the that triggers events during visiting the . Goes through all the components of the specified with its . Computer class instance that is derived from the interface. Goes through all the components of the specified with its . Hardware class instance that is derived from the interface. Goes through all the components of the specified using . Sensor class instance that is derived from the interface. Goes through all the components of the specified . Parameter class instance that is derived from the interface. System enclosure security status based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.4.3. System enclosure state based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.4.2. System enclosure type based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.4.1. Processor family based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.5.2. Processor characteristics based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.5.9. Processor type based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.5.1. Processor socket based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.5.5. System wake-up type based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.2.2. Cache associativity based on DMTF SMBIOS Reference Specification v.3.3.0, Chapter 7.8.5. Processor cache level. Memory type. Initializes a new instance of the class. The data. The strings. Gets the byte. The offset. . Gets the word. The offset. . Gets the dword. The offset. . Gets the qword. The offset. . Gets the string. The offset. . Motherboard BIOS information obtained from the SMBIOS table. Gets the BIOS release date. Gets the size of the physical device containing the BIOS. Gets the string number of the BIOS Vendor’s Name. Gets the string number of the BIOS Version. This value is a free-form string that may contain Core and OEM version information. Gets the size. . Gets the date. The bios date. . System information obtained from the SMBIOS table. Gets the family associated with system. This text string identifies the family to which a particular computer belongs. A family refers to a set of computers that are similar but not identical from a hardware or software point of view. Typically, a family is composed of different computer models, which have different configurations and pricing points. Computers in the same family often have similar branding and cosmetic features. Gets the manufacturer name associated with system. Gets the product name associated with system. Gets the serial number string associated with system. Gets the version string associated with system. Gets System enclosure obtained from the SMBIOS table. Gets the asset tag associated with the enclosure or chassis. Gets Gets or sets the system enclosure lock. System enclosure lock is present if . Otherwise, either a lock is not present or it is unknown if the enclosure has a lock. Gets the string describing the chassis or enclosure manufacturer name. Gets the number of power cords associated with the enclosure or chassis. Gets the state of the enclosure’s power supply (or supplies) when last booted. Gets the height of the enclosure, in 'U's. A U is a standard unit of measure for the height of a rack or rack-mountable component and is equal to 1.75 inches or 4.445 cm. A value of 0 indicates that the enclosure height is unspecified. Gets the physical security status of the enclosure when last booted. Gets the string describing the chassis or enclosure serial number. Gets the string describing the chassis or enclosure SKU number. Gets the thermal state of the enclosure when last booted. Gets Gets the number of null-terminated string representing the chassis or enclosure version. Motherboard information obtained from the SMBIOS table. Gets the value that represents the manufacturer's name. Gets the value that represents the motherboard's name. Gets the value that represents the motherboard's serial number. Gets the value that represents the motherboard's revision number. Processor information obtained from the SMBIOS table. Gets the characteristics of the processor. Gets the value that represents the number of cores per processor socket. Gets the value that represents the number of enabled cores per processor socket. Gets the value that represents the current processor speed (in MHz). Gets the external Clock Frequency, in MHz. If the value is unknown, the field is set to 0. Gets Gets the handle. The handle. Gets the identifier. Gets the L1 cache handle. Gets the L2 cache handle. Gets the L3 cache handle. Gets the string number of Processor Manufacturer. Gets the value that represents the maximum processor speed (in MHz) supported by the system for this processor socket. Gets Gets the value that represents the string number for the serial number of this processor. This value is set by the manufacturer and normally not changeable. Gets Gets the string number for Reference Designation. Gets the value that represents the number of threads per processor socket. Gets the value that represents the string number describing the Processor. Cache information obtained from the SMBIOS table. Gets Gets Gets the handle. Gets the value that represents the installed cache size. Gets the cache designation. . Memory information obtained from the SMBIOS table. Gets the string number of the string that identifies the physically labeled bank where the memory device is located. Gets the string number of the string that identifies the physically-labeled socket or board position where the memory device is located. Gets the string number for the manufacturer of this memory device. Gets the string number for the part number of this memory device. Gets the string number for the serial number of this memory device. Gets the size of the memory device. If the value is 0, no memory device is installed in the socket. Gets the value that identifies the maximum capable speed of the device, in mega transfers per second (MT/s). Gets the type of this memory device. The type. Reads and processes information encoded in an SMBIOS table. Initializes a new instance of the class. Gets Gets Gets Gets Gets Gets Gets Report containing most of the information that could be read from the SMBIOS table. A formatted text string with computer information and the entire SMBIOS table. Helper to calculate the disk performance with base timestamps https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-perfrawdata Gets the SMART data. Gets the SMART attributes. Gets the SMART data. Initializes a new instance of the class. The SMART id of the attribute. The name of the attribute. Initializes a new instance of the class. The SMART id of the attribute. The name of the attribute. A delegate for converting the raw byte array into a value (or null to use the attribute value). Initializes a new instance of the class. The SMART id of the attribute. The name of the attribute. A delegate for converting the raw byte array into a value (or null to use the attribute value). Type of the sensor or null if no sensor is to be created. If there exists more than one attribute with the same sensor channel and type, then a sensor is created only for the first attribute. The name to be used for the sensor, or null if no sensor is created. True to hide the sensor initially. Description for the parameters of the sensor (or null). Gets the SMART identifier. Localization class for SMART attribute names. Reads Smart health status of the drive True, if drive is healthy; False, if unhealthy; Null, if it cannot be read Initializes static members of the class. Gets the processor group count. Returns true if the is valid. The affinity. true if the specified affinity is valid; otherwise, false. Sets the processor group affinity for the current thread. The processor group affinity. The previous processor group affinity. All OK, but need to wait. All OK, but need restart. All OK but need mode change. All OK, but with warning. ADL function completed successfully. Generic Error. Most likely one or more of the Escape calls to the driver failed! ADL not initialized. One of the parameter passed is invalid. One of the parameter size is invalid. Invalid ADL index passed. Invalid controller index passed. Invalid display index passed. Function not supported by the driver. Null Pointer error. Call can't be made due to disabled adapter. Invalid Callback. Display Resource conflict. Failed to update some of the values. Can be returned by set request that include multiple values if not all values were successfully committed. There's no Linux XDisplay in Linux Console environment. If set to 1, then the available spare space has fallen below the threshold. If set to 1, then a temperature is above an over temperature threshold or below an under temperature threshold. If set to 1, then the device reliability has been degraded due to significant media related errors or any internal error that degrades device reliability. If set to 1, then the media has been placed in read only mode If set to 1, then the volatile memory backup device has failed. This field is only valid if the controller has a volatile memory backup solution. Create a instance from a struct with zero initialized memory arrays no need to init every inner array with the correct sizes type of struct that is needed SMART data requested. Identify data is requested. Read SMART data. Read SMART thresholds. obsolete Autosave SMART data. Save SMART attributes. Set SMART to offline immediately. Read SMART log. Write SMART log. Write SMART thresholds. obsolete Enable SMART. Disable SMART. Get SMART status. Set SMART to offline automatically. bit 0:15 Maximum Power (MP) in centiwatts bit 16:23 bit 24 Max Power Scale (MPS), bit 25 Non-Operational State (NOPS) bit 32:63 Entry Latency (ENLAT) in microseconds bit 64:95 Exit Latency (EXLAT) in microseconds bit 96:100 Relative Read Throughput (RRT) bit 104:108 Relative Read Latency (RRL) bit 112:116 Relative Write Throughput (RWT) bit 120:124 Relative Write Latency (RWL) bit 128:143 Idle Power (IDLP) bit 150:151 Idle Power Scale (IPS) bit 152:159 bit 160:175 Active Power (ACTP) bit 176:178 Active Power Workload (APW), bit 182:183 Active Power Scale (APS) bit 184:255. byte 0:1 M - PCI Vendor ID (VID) byte 2:3 M - PCI Subsystem Vendor ID (SSVID) byte 4: 23 M - Serial Number (SN) byte 24:63 M - Model Number (MN) byte 64:71 M - Firmware Revision (FR) byte 72 M - Recommended Arbitration Burst (RAB) byte 73:75 M - IEEE OUI Identifier (IEEE). Controller Vendor code. byte 76 O - Controller Multi-Path I/O and Namespace Sharing Capabilities (CMIC) byte 77 M - Maximum Data Transfer Size (MDTS) byte 78:79 M - Controller ID (CNTLID) byte 80:83 M - Version (VER) byte 84:87 M - RTD3 Resume Latency (RTD3R) byte 88:91 M - RTD3 Entry Latency (RTD3E) byte 92:95 M - Optional Asynchronous Events Supported (OAES) byte 96:239. byte 240:255. Refer to the NVMe Management Interface Specification for definition. byte 256:257 M - Optional Admin Command Support (OACS) byte 258 M - Abort Command Limit (ACL) byte 259 M - Asynchronous Event Request Limit (AERL) byte 260 M - Firmware Updates (FRMW) byte 261 M - Log Page Attributes (LPA) byte 262 M - Error Log Page Entries (ELPE) byte 263 M - Number of Power States Support (NPSS) byte 264 M - Admin Vendor Specific Command Configuration (AVSCC) byte 265 O - Autonomous Power State Transition Attributes (APSTA) byte 266:267 M - Warning Composite Temperature Threshold (WCTEMP) byte 268:269 M - Critical Composite Temperature Threshold (CCTEMP) byte 270:271 O - Maximum Time for Firmware Activation (MTFA) byte 272:275 O - Host Memory Buffer Preferred Size (HMPRE) byte 276:279 O - Host Memory Buffer Minimum Size (HMMIN) byte 280:295 O - Total NVM Capacity (TNVMCAP) byte 296:311 O - Unallocated NVM Capacity (UNVMCAP) byte 312:315 O - Replay Protected Memory Block Support (RPMBS) byte 316:511 byte 512 M - Submission Queue Entry Size (SQES) byte 513 M - Completion Queue Entry Size (CQES) byte 514:515 byte 516:519 M - Number of Namespaces (NN) byte 520:521 M - Optional NVM Command Support (ONCS) byte 522:523 M - Fused Operation Support (FUSES) byte 524 M - Format NVM Attributes (FNA) byte 525 M - Volatile Write Cache (VWC) byte 526:527 M - Atomic Write Unit Normal (AWUN) byte 528:529 M - Atomic Write Unit Power Fail (AWUPF) byte 530 M - NVM Vendor Specific Command Configuration (NVSCC) byte 531 byte 532:533 O - Atomic Compare and Write Unit (ACWU) byte 534:535 byte 536:539 O - SGL Support (SGLS) byte 540:703 byte 704:2047 byte 2048:3071 Power State Descriptors byte 3072:4095 Vendor Specific This field indicates critical warnings for the state of the controller. Each bit corresponds to a critical warning type; multiple bits may be set. Composite Temperature: Contains the temperature of the overall device (controller and NVM included) in units of Kelvin. Available Spare: Contains a normalized percentage (0 to 100%) of the remaining spare capacity available Available Spare Threshold: When the Available Spare falls below the threshold indicated in this field, an asynchronous event completion may occur. The value is indicated as a normalized percentage (0 to 100%). Percentage Used: Contains a vendor specific estimate of the percentage of NVM subsystem life used based on the actual usage and the manufacturer’s prediction of NVM life. A value of 100 indicates that the estimated endurance of the NVM in the NVM subsystem has been consumed, but may not indicate an NVM subsystem failure. The value is allowed to exceed 100. Data Units Read: Contains the number of 512 byte data units the host has read from the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes read) and is rounded up. Data Units Written: Contains the number of 512 byte data units the host has written to the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes written) and is rounded up. Host Read Commands: Contains the number of read commands completed by the controller. For the NVM command set, this is the number of Compare and Read commands. Host Write Commands: Contains the number of write commands completed by the controller. For the NVM command set, this is the number of Write commands. Controller Busy Time: Contains the amount of time the controller is busy with I/O commands. Power Cycles: Contains the number of power cycles. Power On Hours: Contains the number of power-on hours. This does not include time that the controller was powered and in a low power state condition. Unsafe Shutdowns: Contains the number of unsafe shutdowns. This count is incremented when a shutdown notification is not received prior to loss of power. Media Errors: Contains the number of occurrences where the controller detected an unrecoverable data integrity error. Errors such as uncorrectable ECC, CRC checksum failure, or LBA tag mismatch are included in this field. Number of Error Information Log Entries: Contains the number of Error Information log entries over the life of the controller Warning Composite Temperature Time: Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater than or equal to the Warning Composite Temperature Threshold. Critical Composite Temperature Time: Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater than the Critical Composite Temperature Threshold. Contains the current temperature reported by temperature sensor 1-8. Gets the resulting IO control code. Initializes a new instance of the struct. Type of the device. The function. The access. Initializes a new instance of the struct. Type of the device. The function. The method. The access. The operation was successful NvidiaML was not first initialized with nvmlInit() A supplied argument is invalid The requested operation is not available on target device The current user does not have permission for operation A query to find an object was unsuccessful An input argument is not large enough A device's external power cables are not properly attached NVIDIA driver is not loaded User provided timeout passed NVIDIA Kernel detected an interrupt issue with a GPU NvidiaML Shared Library couldn't be found or loaded Local version of NvidiaML doesn't implement this function infoROM is corrupted The GPU has fallen off the bus or has otherwise become inaccessible The GPU requires a reset before it can be used again The GPU control device has been blocked by the operating system/cgroups RM detects a driver/library version mismatch An operation cannot be performed because the GPU is currently in use An public driver error occurred Driver with access at kernel level. Contains Win32 definitions for Windows NT. Describes a local identifier for an adapter. Represents a 64-bit signed integer value. Contains basic information about the operating system. Statically checks if the current system and . Gets information about whether the current system is 64 bit. Gets information about whether the current system is Unix based. ================================================ FILE: OpenHardwareMonitorApi/OpenHardwareMonitorApi.vcxproj ================================================  Debug ARM64EC Debug Win32 Release ARM64EC Release Win32 Debug x64 Release x64 {C0A42F4A-ABB3-4575-B4D5-CEDD8379AC26} v4.7.2 ManagedCProj OpenHardwareMonitorApi 10.0 DynamicLibrary true v143 true Unicode DynamicLibrary false v143 true Unicode DynamicLibrary true v143 true Unicode DynamicLibrary true v143 true Unicode DynamicLibrary false v143 true Unicode DynamicLibrary false v143 true Unicode true $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Configuration)\ true $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\ true $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\ false $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Configuration)\ false $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\ false $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\ Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions);OPENHARDWAREMONITOR_EXPORTS Use Level3 Disabled _DEBUG;%(PreprocessorDefinitions);OPENHARDWAREMONITOR_EXPORTS Use Level3 Disabled _DEBUG;%(PreprocessorDefinitions);OPENHARDWAREMONITOR_EXPORTS Use Level3 WIN32;NDEBUG;%(PreprocessorDefinitions);OPENHARDWAREMONITOR_EXPORTS Use Level3 NDEBUG;%(PreprocessorDefinitions);OPENHARDWAREMONITOR_EXPORTS Use Level3 NDEBUG;%(PreprocessorDefinitions);OPENHARDWAREMONITOR_EXPORTS Use LibreHardwareMonitorLib.dll Create Create Create Create Create Create ================================================ FILE: OpenHardwareMonitorApi/OpenHardwareMonitorApi.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 头文件 头文件 头文件 头文件 头文件 头文件 源文件 源文件 源文件 资源文件 ================================================ FILE: OpenHardwareMonitorApi/OpenHardwareMonitorImp.cpp ================================================ // 这是主 DLL 文件。 #include "stdafx.h" #include "OpenHardwareMonitorImp.h" #include namespace OpenHardwareMonitorApi { static std::wstring error_message; //将CRL的String类型转换成C++的std::wstring类型 static std::wstring ClrStringToStdWstring(System::String^ str) { if (str == nullptr) { return std::wstring(); } else { const wchar_t* chars = (const wchar_t*)(Runtime::InteropServices::Marshal::StringToHGlobalUni(str)).ToPointer(); std::wstring os = chars; Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr((void*)chars)); return os; } } std::shared_ptr CreateInstance() { std::shared_ptr pMonitor; try { MonitorGlobal::Instance()->Init(); pMonitor = std::make_shared(); } catch (System::Exception^ e) { error_message = ClrStringToStdWstring(e->Message); } return pMonitor; } std::wstring GetErrorMessage() { return error_message; } float COpenHardwareMonitor::CpuTemperature() { return m_cpu_temperature; } float COpenHardwareMonitor::GpuTemperature() { if (m_gpu_nvidia_temperature >= 0) return m_gpu_nvidia_temperature; else if (m_gpu_ati_temperature >= 0) return m_gpu_ati_temperature; else return m_gpu_intel_temperature; } float COpenHardwareMonitor::HDDTemperature() { return m_hdd_temperature; } float COpenHardwareMonitor::MainboardTemperature() { return m_main_board_temperature; } float COpenHardwareMonitor::GpuUsage() { if (m_gpu_nvidia_usage >= 0) return m_gpu_nvidia_usage; else if (m_gpu_ati_usage >= 0) return m_gpu_ati_usage; else return m_gpu_intel_usage; } float COpenHardwareMonitor::CpuFreq() { return m_cpu_freq; } float COpenHardwareMonitor::CpuUsage() { return m_cpu_usage; } const std::map& COpenHardwareMonitor::AllHDDTemperature() { return m_all_hdd_temperature; } const std::map& COpenHardwareMonitor::AllCpuTemperature() { return m_all_cpu_temperature; } const std::map& COpenHardwareMonitor::AllHDDUsage() { return m_all_hdd_usage; } void COpenHardwareMonitor::SetCpuEnable(bool enable) { MonitorGlobal::Instance()->computer->IsCpuEnabled = enable; } void COpenHardwareMonitor::SetGpuEnable(bool enable) { MonitorGlobal::Instance()->computer->IsGpuEnabled = enable; } void COpenHardwareMonitor::SetHddEnable(bool enable) { MonitorGlobal::Instance()->computer->IsStorageEnabled = enable; } void COpenHardwareMonitor::SetMainboardEnable(bool enable) { MonitorGlobal::Instance()->computer->IsMotherboardEnabled = enable; } bool COpenHardwareMonitor::GetCPUFreq(IHardware^ hardware, float& freq) { for (int i = 0; i < hardware->Sensors->Length; i++) { if (hardware->Sensors[i]->SensorType == SensorType::Clock) { String^ name = hardware->Sensors[i]->Name; if (name != L"Bus Speed") m_all_cpu_clock[ClrStringToStdWstring(name)] = Convert::ToDouble(hardware->Sensors[i]->Value); } } float sum{}; for (auto i : m_all_cpu_clock) sum += i.second; freq = sum / m_all_cpu_clock.size() / 1000.0; return true; } bool COpenHardwareMonitor::GetCpuUsage(IHardware^ hardware, float& cpu_usage) { for (int i = 0; i < hardware->Sensors->Length; i++) { if (hardware->Sensors[i]->SensorType == SensorType::Load) { String^ name = hardware->Sensors[i]->Name; if (name != L"CPU Total") { cpu_usage = Convert::ToDouble(hardware->Sensors[i]->Value); return true; } } } return false; } bool COpenHardwareMonitor::GetHardwareTemperature(IHardware^ hardware, float& temperature) { temperature = -1; std::vector all_temperature; float core_temperature{ -1 }; System::String^ temperature_name; switch (hardware->HardwareType) { case HardwareType::Cpu: temperature_name = L"Core Average"; break; case HardwareType::GpuNvidia: case HardwareType::GpuAmd: case HardwareType::GpuIntel: temperature_name = L"GPU Core"; break; default: break; } for (int i = 0; i < hardware->Sensors->Length; i++) { //找到温度传感器 if (hardware->Sensors[i]->SensorType == SensorType::Temperature) { float cur_temperture = Convert::ToDouble(hardware->Sensors[i]->Value); all_temperature.push_back(cur_temperture); if (hardware->Sensors[i]->Name == temperature_name) //如果找到了名称为temperature_name的温度传感器,则将温度保存到core_temperature里 core_temperature = cur_temperture; } } if (core_temperature >= 0) { temperature = core_temperature; return true; } if (!all_temperature.empty()) { //如果有多个温度传感器,则取平均值 float sum{}; for (auto i : all_temperature) sum += i; temperature = sum / all_temperature.size(); return true; } //如果没有找到温度传感器,则在SubHardware中寻找 for (int i = 0; i < hardware->SubHardware->Length; i++) { if (GetHardwareTemperature(hardware->SubHardware[i], temperature)) return true; } return false; } bool COpenHardwareMonitor::GetCpuTemperature(IHardware^ hardware, float& temperature) { temperature = -1; m_all_cpu_temperature.clear(); for (int i = 0; i < hardware->Sensors->Length; i++) { //找到温度传感器 if (hardware->Sensors[i]->SensorType == SensorType::Temperature) { String^ name = hardware->Sensors[i]->Name; //保存每个CPU传感器的温度 m_all_cpu_temperature[ClrStringToStdWstring(name)] = Convert::ToDouble(hardware->Sensors[i]->Value); } } //计算平均温度 if (!m_all_cpu_temperature.empty()) { float sum{}; for (const auto& item : m_all_cpu_temperature) sum += item.second; temperature = sum / m_all_cpu_temperature.size(); } return temperature > 0; } bool COpenHardwareMonitor::GetGpuUsage(IHardware^ hardware, float& gpu_usage) { float usage_max = 0; for (int i = 0; i < hardware->Sensors->Length; i++) { //找到负载 if (hardware->Sensors[i]->SensorType == SensorType::Load) { float cur_gpu_usage = Convert::ToDouble(hardware->Sensors[i]->Value); if (hardware->Sensors[i]->Name == L"GPU Core") { gpu_usage = cur_gpu_usage; return true; } //计算最大值 if (cur_gpu_usage > usage_max) usage_max = cur_gpu_usage; } } gpu_usage = usage_max; return true; } bool COpenHardwareMonitor::GetHddUsage(IHardware^ hardware, float& hdd_usage) { for (int i = 0; i < hardware->Sensors->Length; i++) { //找到负载 if (hardware->Sensors[i]->SensorType == SensorType::Load) { if (hardware->Sensors[i]->Name == L"Total Activity") { hdd_usage = Convert::ToDouble(hardware->Sensors[i]->Value); return true; } } } return false; } COpenHardwareMonitor::COpenHardwareMonitor() { ResetAllValues(); } COpenHardwareMonitor::~COpenHardwareMonitor() { MonitorGlobal::Instance()->UnInit(); } void COpenHardwareMonitor::ResetAllValues() { m_cpu_temperature = -1; m_gpu_nvidia_temperature = -1; m_gpu_ati_temperature = -1; m_gpu_intel_temperature = -1; m_hdd_temperature = -1; m_main_board_temperature = -1; m_gpu_nvidia_usage = -1; m_gpu_ati_usage = -1; m_gpu_intel_usage = -1; m_all_hdd_temperature.clear(); m_all_hdd_usage.clear(); m_cpu_freq = -1; m_cpu_usage = -1; } void COpenHardwareMonitor::InsertValueToMap(std::map& value_map, const std::wstring& key, float value) { auto iter = value_map.find(key); if (iter == value_map.end()) { value_map[key] = value; } else { std::wstring key_exist = iter->first; size_t index = key_exist.rfind(L'#'); //查找字符串是否含有#号 if (index != std::wstring::npos) { //取到#号后面的数字,将其加1 int num = _wtoi(key_exist.substr(index + 1).c_str()); num++; key_exist = key_exist.substr(0, index + 1); key_exist += std::to_wstring(num); } else //没有#号则在末尾添加" #1" { key_exist += L" #1"; } value_map[key_exist] = value; } } void COpenHardwareMonitor::GetHardwareInfo() { ResetAllValues(); error_message.clear(); try { auto computer = MonitorGlobal::Instance()->computer; computer->Accept(MonitorGlobal::Instance()->updateVisitor); for (int i = 0; i < computer->Hardware->Count; i++) { //查找硬件类型 switch (computer->Hardware[i]->HardwareType) { case HardwareType::Cpu: if (m_cpu_temperature < 0) GetCpuTemperature(computer->Hardware[i], m_cpu_temperature); if (m_cpu_freq < 0) GetCPUFreq(computer->Hardware[i], m_cpu_freq); if (m_cpu_usage < 0) GetCpuUsage(computer->Hardware[i], m_cpu_usage); break; case HardwareType::GpuNvidia: if (m_gpu_nvidia_temperature < 0) GetHardwareTemperature(computer->Hardware[i], m_gpu_nvidia_temperature); if (m_gpu_nvidia_usage < 0) GetGpuUsage(computer->Hardware[i], m_gpu_nvidia_usage); break; case HardwareType::GpuAmd: if (m_gpu_ati_temperature < 0) GetHardwareTemperature(computer->Hardware[i], m_gpu_ati_temperature); if (m_gpu_ati_usage < 0) GetGpuUsage(computer->Hardware[i], m_gpu_ati_usage); break; case HardwareType::GpuIntel: if (m_gpu_intel_temperature < 0) GetHardwareTemperature(computer->Hardware[i], m_gpu_intel_temperature); if (m_gpu_intel_usage < 0) GetGpuUsage(computer->Hardware[i], m_gpu_intel_usage); break; case HardwareType::Storage: { float cur_hdd_temperature = -1; GetHardwareTemperature(computer->Hardware[i], cur_hdd_temperature); //m_all_hdd_temperature[ClrStringToStdWstring(computer->Hardware[i]->Name)] = cur_hdd_temperature; InsertValueToMap(m_all_hdd_temperature, ClrStringToStdWstring(computer->Hardware[i]->Name), cur_hdd_temperature); float cur_hdd_usage = -1; GetHddUsage(computer->Hardware[i], cur_hdd_usage); //m_all_hdd_usage[ClrStringToStdWstring(computer->Hardware[i]->Name)] = cur_hdd_usage; InsertValueToMap(m_all_hdd_usage, ClrStringToStdWstring(computer->Hardware[i]->Name), cur_hdd_usage); if (m_hdd_temperature < 0) m_hdd_temperature = cur_hdd_temperature; } break; case HardwareType::Motherboard: if (m_main_board_temperature < 0) GetHardwareTemperature(computer->Hardware[i], m_main_board_temperature); break; default: break; } } } catch (System::Exception^ e) { error_message = ClrStringToStdWstring(e->Message); } } //////////////////////////////////////////////////////////////////////////////////// MonitorGlobal::MonitorGlobal() { } MonitorGlobal::~MonitorGlobal() { } void MonitorGlobal::Init() { updateVisitor = gcnew UpdateVisitor(); computer = gcnew Computer(); computer->Open(); } void MonitorGlobal::UnInit() { computer->Close(); } } ================================================ FILE: OpenHardwareMonitorApi/OpenHardwareMonitorImp.h ================================================ // OpenHardwareMonitorApi.h #pragma once #include #include "OpenHardwareMonitor/OpenHardwareMonitorApi.h" #include "UpdateVisitor.h" #include using namespace System; using namespace LibreHardwareMonitor::Hardware; namespace OpenHardwareMonitorApi { public class COpenHardwareMonitor : public IOpenHardwareMonitor { public: COpenHardwareMonitor(); virtual ~COpenHardwareMonitor(); virtual void GetHardwareInfo() override; virtual float CpuTemperature() override; virtual float GpuTemperature() override; virtual float HDDTemperature() override; virtual float MainboardTemperature() override; virtual float GpuUsage() override; virtual float CpuFreq() override; virtual float CpuUsage() override; virtual const std::map& AllHDDTemperature() override; virtual const std::map& AllCpuTemperature() override; virtual const std::map& AllHDDUsage() override; virtual void SetCpuEnable(bool enable) override; virtual void SetGpuEnable(bool enable) override; virtual void SetHddEnable(bool enable) override; virtual void SetMainboardEnable(bool enable) override; private: bool GetHardwareTemperature(IHardware^ hardware, float& temperature); bool GetCpuTemperature(IHardware^ hardware, float& temperature); bool GetGpuUsage(IHardware^ hardware, float& gpu_usage); bool GetHddUsage(IHardware^ hardware, float& hdd_usage); bool GetCPUFreq(IHardware^ hardware, float& freq); bool GetCpuUsage(IHardware^ hardware, float& cpu_usage); void ResetAllValues(); //向map中插入一个数值,如果key已经存在,则自动对新插入的key重命名 static void InsertValueToMap(std::map& value_map, const std::wstring& key, float value); private: float m_cpu_temperature{}; float m_gpu_nvidia_temperature{}; float m_gpu_ati_temperature{}; float m_gpu_intel_temperature{}; float m_hdd_temperature{}; float m_main_board_temperature{}; float m_gpu_nvidia_usage{}; float m_gpu_ati_usage{}; float m_gpu_intel_usage{}; float m_cpu_freq{}; float m_cpu_usage{}; std::map m_all_hdd_temperature; std::map m_all_cpu_temperature; std::map m_all_cpu_clock; std::map m_all_hdd_usage; }; //一个单实例类 //由于COpenHardwareMonitor是非托管类,不能将托管类的对象作为成员变量,此类用于保存托管类的对象 public ref class MonitorGlobal { public: MonitorGlobal(); ~MonitorGlobal(); static MonitorGlobal^ Instance() { if (m_instance == nullptr) { m_instance = gcnew MonitorGlobal(); } return m_instance; } void Init(); void UnInit(); Computer^ computer; UpdateVisitor^ updateVisitor{}; private: static MonitorGlobal^ m_instance{}; }; } ================================================ FILE: OpenHardwareMonitorApi/ReadMe.txt ================================================ ======================================================================== DYNAMIC LINK LIBRARY : OpenHardwareMonitorApi Project Overview ======================================================================== AppWizard has created this OpenHardwareMonitorApi DLL for you. This file contains a summary of what you will find in each of the files that make up your OpenHardwareMonitorApi application. OpenHardwareMonitorApi.vcxproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. OpenHardwareMonitorApi.vcxproj.filters This is the filters file for VC++ projects generated using an Application Wizard. It contains information about the association between the files in your project and the filters. This association is used in the IDE to show grouping of files with similar extensions under a specific node (for e.g. ".cpp" files are associated with the "Source Files" filter). OpenHardwareMonitorApi.cpp This is the main DLL source file. OpenHardwareMonitorApi.h This file contains a class declaration. AssemblyInfo.cpp Contains custom attributes for modifying assembly metadata. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: OpenHardwareMonitorApi/Stdafx.cpp ================================================ // stdafx.cpp : ֻ׼ļԴļ // OpenHardwareMonitorApi.pch ΪԤͷ // stdafx.obj ԤϢ #include "stdafx.h" ================================================ FILE: OpenHardwareMonitorApi/Stdafx.h ================================================ // stdafx.h : ׼ϵͳļİļ // Ǿʹõĵ // ضĿİļ #pragma once ================================================ FILE: OpenHardwareMonitorApi/UpdateVisitor.cpp ================================================ #include "stdafx.h" #include "UpdateVisitor.h" namespace OpenHardwareMonitorApi { void UpdateVisitor::VisitComputer(IComputer ^ computer) { computer->Traverse(this); } void UpdateVisitor::VisitHardware(IHardware ^ hardware) { hardware->Update(); for each (IHardware^ subHardware in hardware->SubHardware) { subHardware->Accept(this); } } void UpdateVisitor::VisitSensor(ISensor ^ sensor) { } void UpdateVisitor::VisitParameter(IParameter ^ parameter) { } } ================================================ FILE: OpenHardwareMonitorApi/UpdateVisitor.h ================================================ #pragma once using namespace LibreHardwareMonitor::Hardware; namespace OpenHardwareMonitorApi { public ref class UpdateVisitor : IVisitor { public: virtual void VisitComputer(IComputer^ computer); virtual void VisitHardware(IHardware^ hardware); virtual void VisitSensor(ISensor^ sensor); virtual void VisitParameter(IParameter^ parameter); }; } ================================================ FILE: OpenHardwareMonitorApi/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by app.rc ================================================ FILE: PluginDemo/CustomDrawItem.cpp ================================================ #include "pch.h" #include "CustomDrawItem.h" #include "DataManager.h" const wchar_t* CCustomDrawItem::GetItemName() const { return CDataManager::Instance().StringRes(IDS_CUSTOM_DRAW_ITEM); } const wchar_t* CCustomDrawItem::GetItemId() const { return L"4Tc21hGS"; } const wchar_t* CCustomDrawItem::GetItemLableText() const { return L""; } const wchar_t* CCustomDrawItem::GetItemValueText() const { return L""; } const wchar_t* CCustomDrawItem::GetItemValueSampleText() const { return L""; } bool CCustomDrawItem::IsCustomDraw() const { return true; } int CCustomDrawItem::GetItemWidth() const { return 50; } static void DrawLine(CDC* pDC, CPoint point1, CPoint point2, COLORREF color) { CPen aPen, * pOldPen; aPen.CreatePen(PS_SOLID, 1, color); pOldPen = pDC->SelectObject(&aPen); pDC->MoveTo(point1); pDC->LineTo(point2); pDC->SelectObject(pOldPen); } void CCustomDrawItem::DrawItem(void* hDC, int x, int y, int w, int h, bool dark_mode) { //绘图句柄 CDC* pDC = CDC::FromHandle((HDC)hDC); //矩形区域 CRect rect(CPoint(x, y), CSize(w, h)); //设置颜色 COLORREF color1{ dark_mode ? RGB(255, 143, 107) : RGB(227, 81, 16) }; COLORREF color2{ dark_mode ? RGB(183, 241, 96) : RGB(83, 131, 11) }; COLORREF color3{ dark_mode ? RGB(158, 218, 251) : RGB(6, 111, 168) }; //显示时、分、秒的矩形区域 CRect rect1{ rect }, rect2{ rect }, rect3{ rect }; rect1.bottom = rect.top + rect.Height() / 3; rect2 = rect1; rect2.MoveToY(rect1.bottom); rect3.top = rect2.bottom; rect1.DeflateRect(1, 1); rect2.DeflateRect(1, 1); rect3.DeflateRect(1, 1); //根据当前时间计算矩形的宽度 SYSTEMTIME& time{ CDataManager::Instance().m_system_time }; int hour_width{ static_cast((time.wHour + time.wMinute / 60.0) * w / 24) }; int min_width{ static_cast((time.wMinute + time.wSecond / 60.0) * w / 60) }; int sec_width{ time.wSecond * w / 60 }; rect1.right = rect1.left + hour_width; rect2.right = rect2.left + min_width; rect3.right = rect3.left + sec_width; //填充矩形 pDC->FillSolidRect(rect1, color1); pDC->FillSolidRect(rect2, color2); pDC->FillSolidRect(rect3, color3); //绘制刻度 COLORREF color_scale{ dark_mode ? RGB(225, 225, 225) : RGB(45, 45, 45) }; for (int i{}; i < 24; i++) { int x_pos{ i * w / 24 + rect1.left }; DrawLine(pDC, CPoint(x_pos, rect1.top), CPoint(x_pos, i % 6 == 0 ? rect1.bottom : rect1.top + rect1.Height() / 2), color_scale); } } ================================================ FILE: PluginDemo/CustomDrawItem.h ================================================ #pragma once #include "PluginInterface.h" class CCustomDrawItem : public IPluginItem { // 通过 IPluginItem 继承 virtual const wchar_t* GetItemName() const override; virtual const wchar_t* GetItemId() const override; virtual const wchar_t* GetItemLableText() const override; virtual const wchar_t* GetItemValueText() const override; virtual const wchar_t* GetItemValueSampleText() const override; virtual bool IsCustomDraw() const override; virtual int GetItemWidth() const override; virtual void DrawItem(void* hDC, int x, int y, int w, int h, bool dark_mode) override; }; ================================================ FILE: PluginDemo/DataManager.cpp ================================================ #include "pch.h" #include "DataManager.h" CDataManager CDataManager::m_instance; CDataManager::CDataManager() { } CDataManager::~CDataManager() { SaveConfig(); } CDataManager& CDataManager::Instance() { return m_instance; } void CDataManager::LoadConfig(const std::wstring& config_dir) { //获取模块的路径 HMODULE hModule = reinterpret_cast(&__ImageBase); wchar_t path[MAX_PATH]; GetModuleFileNameW(hModule, path, MAX_PATH); std::wstring module_path = path; m_config_path = module_path; if (!config_dir.empty()) { size_t index = module_path.find_last_of(L"\\/"); //模块的文件名 std::wstring module_file_name = module_path.substr(index + 1); m_config_path = config_dir + module_file_name; } m_config_path += L".ini"; m_setting_data.show_second = GetPrivateProfileInt(_T("config"), _T("show_second"), 0, m_config_path.c_str()); //m_setting_data.show_label_text = GetPrivateProfileInt(_T("config"), _T("show_label_text"), 1, config_path.c_str()); } static void WritePrivateProfileInt(const wchar_t* app_name, const wchar_t* key_name, int value, const wchar_t* file_path) { wchar_t buff[16]; swprintf_s(buff, L"%d", value); WritePrivateProfileString(app_name, key_name, buff, file_path); } void CDataManager::SaveConfig() const { WritePrivateProfileInt(_T("config"), _T("show_second"), m_setting_data.show_second, m_config_path.c_str()); //WritePrivateProfileInt(_T("config"), _T("show_label_text"), m_setting_data.show_label_text, config_path.c_str()); } const CString& CDataManager::StringRes(UINT id) { auto iter = m_string_table.find(id); if (iter != m_string_table.end()) { return iter->second; } else { AFX_MANAGE_STATE(AfxGetStaticModuleState()); m_string_table[id].LoadString(id); return m_string_table[id]; } } ================================================ FILE: PluginDemo/DataManager.h ================================================ #pragma once #include #include #define g_data CDataManager::Instance() struct SettingData { bool show_second{}; //bool show_label_text{}; }; class CDataManager { private: CDataManager(); ~CDataManager(); public: static CDataManager& Instance(); void LoadConfig(const std::wstring& config_dir); void SaveConfig() const; const CString& StringRes(UINT id); //根据资源id获取一个字符串资源 public: std::wstring m_cur_time; std::wstring m_cur_date; SYSTEMTIME m_system_time; SettingData m_setting_data; private: static CDataManager m_instance; std::wstring m_config_path; std::map m_string_table; }; ================================================ FILE: PluginDemo/OptionsDlg.cpp ================================================ // OptionsDlg.cpp: 实现文件 // #include "pch.h" #include "PluginDemo.h" #include "OptionsDlg.h" #include "afxdialogex.h" // COptionsDlg 对话框 IMPLEMENT_DYNAMIC(COptionsDlg, CDialog) COptionsDlg::COptionsDlg(CWnd* pParent /*=nullptr*/) : CDialog(IDD_OPTIONS_DIALOG, pParent) { } COptionsDlg::~COptionsDlg() { } void COptionsDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(COptionsDlg, CDialog) ON_BN_CLICKED(IDC_SHOW_SECOND_CHECK, &COptionsDlg::OnBnClickedShowSecondCheck) END_MESSAGE_MAP() // COptionsDlg 消息处理程序 BOOL COptionsDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 //初始化控件状态 CheckDlgButton(IDC_SHOW_SECOND_CHECK, m_data.show_second); //CheckDlgButton(IDC_SHOW_LABEL_CHECK, m_data.show_label_text); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void COptionsDlg::OnBnClickedShowSecondCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.show_second = (IsDlgButtonChecked(IDC_SHOW_SECOND_CHECK) != 0); } ================================================ FILE: PluginDemo/OptionsDlg.h ================================================ #pragma once #include "DataManager.h" // COptionsDlg 对话框 class COptionsDlg : public CDialog { DECLARE_DYNAMIC(COptionsDlg) public: COptionsDlg(CWnd* pParent = nullptr); // 标准构造函数 virtual ~COptionsDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_OPTIONS_DIALOG }; #endif SettingData m_data; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnBnClickedShowSecondCheck(); }; ================================================ FILE: PluginDemo/PluginDemo.cpp ================================================ #include "pch.h" #include "PluginDemo.h" #include "DataManager.h" #include "OptionsDlg.h" CPluginDemo CPluginDemo::m_instance; CPluginDemo::CPluginDemo() { } CPluginDemo& CPluginDemo::Instance() { return m_instance; } IPluginItem* CPluginDemo::GetItem(int index) { switch (index) { case 0: return &m_system_date; case 1: return &m_system_time; case 2: return &m_custom_draw_item; default: break; } return nullptr; } void CPluginDemo::DataRequired() { //获取时间和日期 SYSTEMTIME& system_time{ CDataManager::Instance().m_system_time }; GetLocalTime(&system_time); wchar_t buff[128]; swprintf_s(buff, L"%d/%.2d/%.2d", system_time.wYear, system_time.wMonth, system_time.wDay); CDataManager::Instance().m_cur_date = buff; if (CDataManager::Instance().m_setting_data.show_second) swprintf_s(buff, L"%.2d:%.2d:%.2d", system_time.wHour, system_time.wMinute, system_time.wSecond); else swprintf_s(buff, L"%.2d:%.2d", system_time.wHour, system_time.wMinute); CDataManager::Instance().m_cur_time = buff; } const wchar_t* CPluginDemo::GetInfo(PluginInfoIndex index) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); static CString str; switch (index) { case TMI_NAME: str.LoadString(IDS_PLUGIN_NAME); return str.GetString(); case TMI_DESCRIPTION: str.LoadString(IDS_PLUGIN_DESCRIPTION); return str.GetString(); case TMI_AUTHOR: return L"zhongyang219"; case TMI_COPYRIGHT: return L"Copyright (C) by Zhong Yang 2021"; case TMI_VERSION: return L"1.0"; case ITMPlugin::TMI_URL: return L"https://github.com/zhongyang219/TrafficMonitor"; break; default: break; } return L""; } ITMPlugin::OptionReturn CPluginDemo::ShowOptionsDialog(void* hParent) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); COptionsDlg dlg(CWnd::FromHandle((HWND)hParent)); dlg.m_data = CDataManager::Instance().m_setting_data; if (dlg.DoModal() == IDOK) { CDataManager::Instance().m_setting_data = dlg.m_data; return ITMPlugin::OR_OPTION_CHANGED; } return ITMPlugin::OR_OPTION_UNCHANGED; } void CPluginDemo::OnExtenedInfo(ExtendedInfoIndex index, const wchar_t* data) { switch (index) { case ITMPlugin::EI_CONFIG_DIR: //从配置文件读取配置 g_data.LoadConfig(std::wstring(data)); break; default: break; } } void CPluginDemo::OnInitialize(ITrafficMonitor* pApp) { m_app = pApp; std::wstring str = m_app->GetStringRes(L"IDS_MEMORY_USAGE", L"text"); std::wstring str1 = m_app->GetStringRes(L"BCP_47", L"general"); int a = 0; } ITMPlugin* TMPluginGetInstance() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return &CPluginDemo::Instance(); } ================================================ FILE: PluginDemo/PluginDemo.h ================================================ #pragma once #include "PluginInterface.h" #include "PluginSystemDate.h" #include "PluginSystemTime.h" #include "CustomDrawItem.h" class CPluginDemo : public ITMPlugin { private: CPluginDemo(); public: static CPluginDemo& Instance(); // 通过 ITMPlugin 继承 virtual IPluginItem* GetItem(int index) override; virtual void DataRequired() override; virtual const wchar_t* GetInfo(PluginInfoIndex index) override; virtual OptionReturn ShowOptionsDialog(void* hParent) override; virtual void OnExtenedInfo(ExtendedInfoIndex index, const wchar_t* data) override; virtual void OnInitialize(ITrafficMonitor* pApp) override; private: CPluginSystemDate m_system_date; CPluginSystemTime m_system_time; CCustomDrawItem m_custom_draw_item; ITrafficMonitor* m_app{}; static CPluginDemo m_instance; }; #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) ITMPlugin* TMPluginGetInstance(); #ifdef __cplusplus } #endif ================================================ FILE: PluginDemo/PluginDemo.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // (壬й) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_OPTIONS_DIALOG DIALOGEX 0, 0, 165, 77 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ʱں" FONT 9, "΢ź", 400, 0, 0x0 BEGIN DEFPUSHBUTTON "ȷ",IDOK,50,56,50,14 PUSHBUTTON "ȡ",IDCANCEL,108,56,50,14 GROUPBOX "ʽ",IDC_STATIC,7,7,151,45 CONTROL "ʾ",IDC_SHOW_SECOND_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,21,63,10 END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_OPTIONS_DIALOG, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 158 TOPMARGIN, 7 BOTTOMMARGIN, 70 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // AFX_DIALOG_LAYOUT // IDD_OPTIONS_DIALOG AFX_DIALOG_LAYOUT BEGIN 0 END ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,1 PRODUCTVERSION 1,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080404b0" BEGIN VALUE "CompanyName", "By ZhongYang" VALUE "FileDescription", "Plugin Demo for TrafficMonitor" VALUE "FileVersion", "1.0.0.1" VALUE "InternalName", "PluginDe.dll" VALUE "LegalCopyright", "Copyright (C) 2021 By ZhongYang" VALUE "OriginalFilename", "PluginDe.dll" VALUE "ProductName", "Plugin Demo for TrafficMonitor" VALUE "ProductVersion", "1.0.0.1" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x804, 1200 END END ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN IDS_PLUGIN_NAME "TrafficMonitorʾ" IDS_PLUGIN_DESCRIPTION "TrafficMonitorʾΪ߿TrafficMonitorṩ" IDS_TIME "ʱ" IDS_DATE "" IDS_CUSTOM_DRAW_ITEM "Իʾ" END #endif // (壬й) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Ӣ() resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_OPTIONS_DIALOG DIALOGEX 0, 0, 165, 74 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Time and date settings" FONT 9, "Segoe UI", 400, 0, 0x0 BEGIN DEFPUSHBUTTON "OK",IDOK,50,53,50,14 PUSHBUTTON "Cancel",IDCANCEL,108,53,50,14 GROUPBOX "Format",IDC_STATIC,7,7,151,42 CONTROL "Show seconds",IDC_SHOW_SECOND_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,21,63,10 END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_OPTIONS_DIALOG, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 158 TOPMARGIN, 7 BOTTOMMARGIN, 67 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // AFX_DIALOG_LAYOUT // IDD_OPTIONS_DIALOG AFX_DIALOG_LAYOUT BEGIN 0 END ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN IDS_PLUGIN_NAME "A sample plug-in for TrafficMonitor." IDS_PLUGIN_DESCRIPTION "A sample plug-in for TrafficMonitor, providing an example for developers to develop TrafficMonitor plug-ins." IDS_TIME "Time" IDS_DATE "Date" IDS_CUSTOM_DRAW_ITEM "Custom draw example" END #endif // Ӣ() resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: PluginDemo/PluginDemo.vcxproj ================================================ Debug ARM64EC Debug Win32 Release ARM64EC Release Win32 Debug x64 Release x64 16.0 Win32Proj {d1ca3ecc-dc32-445a-b734-c4db08d4ba34} PluginDemo 10.0 DynamicLibrary true v143 Unicode Dynamic DynamicLibrary false v143 true Unicode Dynamic DynamicLibrary true v143 Unicode Dynamic DynamicLibrary true v143 Unicode Dynamic DynamicLibrary false v143 true Unicode Dynamic DynamicLibrary false v143 true Unicode Dynamic true $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Configuration)\plugins\ false $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Configuration)\plugins\ true $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\plugins\ true $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\plugins\ false $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\plugins\ false $(ProjectDir)..\include;$(IncludePath) $(SolutionDir)Bin\$(Platform)\$(Configuration)\plugins\ Level3 true WIN32;_DEBUG;PLUGINDEMO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h true Windows true false Level3 true true true WIN32;NDEBUG;PLUGINDEMO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h true Windows true true true false Level3 true _DEBUG;PLUGINDEMO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h true Windows true false Level3 true _DEBUG;PLUGINDEMO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h true Windows true false Level3 true true true NDEBUG;PLUGINDEMO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h true Windows true true true false Level3 true true true NDEBUG;PLUGINDEMO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h true Windows true true true false Create Create Create Create Create Create ================================================ FILE: PluginDemo/PluginDemo.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 头文件 头文件 头文件 头文件 头文件 头文件 头文件 头文件 头文件 头文件 源文件 源文件 源文件 源文件 源文件 源文件 源文件 资源文件 ================================================ FILE: PluginDemo/PluginSystemDate.cpp ================================================ #include "pch.h" #include #include "PluginSystemDate.h" #include "DataManager.h" CPluginSystemDate::CPluginSystemDate() { } const wchar_t* CPluginSystemDate::GetItemName() const { return CDataManager::Instance().StringRes(IDS_DATE); } const wchar_t* CPluginSystemDate::GetItemId() const { return L"uQI0sH6a"; } const wchar_t* CPluginSystemDate::GetItemLableText() const { return CDataManager::Instance().StringRes(IDS_DATE); } const wchar_t* CPluginSystemDate::GetItemValueText() const { return CDataManager::Instance().m_cur_date.c_str(); } const wchar_t* CPluginSystemDate::GetItemValueSampleText() const { return L"2022/08/08"; } ================================================ FILE: PluginDemo/PluginSystemDate.h ================================================ #pragma once #include "PluginInterface.h" class CPluginSystemDate : public IPluginItem { public: CPluginSystemDate(); // 通过 IPluginItem 继承 virtual const wchar_t* GetItemName() const override; virtual const wchar_t* GetItemId() const override; virtual const wchar_t* GetItemLableText() const override; virtual const wchar_t* GetItemValueText() const override; virtual const wchar_t* GetItemValueSampleText() const override; private: }; ================================================ FILE: PluginDemo/PluginSystemTime.cpp ================================================ #include "pch.h" #include #include "PluginSystemTime.h" #include "DataManager.h" CPluginSystemTime::CPluginSystemTime() { } const wchar_t* CPluginSystemTime::GetItemName() const { return CDataManager::Instance().StringRes(IDS_TIME); } const wchar_t* CPluginSystemTime::GetItemId() const { return L"B3tkxi5d"; } const wchar_t* CPluginSystemTime::GetItemLableText() const { return CDataManager::Instance().StringRes(IDS_TIME); } const wchar_t* CPluginSystemTime::GetItemValueText() const { return CDataManager::Instance().m_cur_time.c_str(); } const wchar_t* CPluginSystemTime::GetItemValueSampleText() const { if (CDataManager::Instance().m_setting_data.show_second) return L"12:00:00"; else return L"12:00"; } int CPluginSystemTime::IsDrawResourceUsageGraph() const { return 1; } float CPluginSystemTime::GetResourceUsageGraphValue() const { float value = CDataManager::Instance().m_system_time.wSecond / 60.0f; return value; } ================================================ FILE: PluginDemo/PluginSystemTime.h ================================================ #pragma once #include "PluginInterface.h" class CPluginSystemTime : public IPluginItem { public: CPluginSystemTime(); public: // 通过 IPluginItem 继承 virtual const wchar_t* GetItemName() const override; virtual const wchar_t* GetItemId() const override; virtual const wchar_t* GetItemLableText() const override; virtual const wchar_t* GetItemValueText() const override; virtual const wchar_t* GetItemValueSampleText() const override; virtual int IsDrawResourceUsageGraph() const override; virtual float GetResourceUsageGraphValue() const override; private: }; ================================================ FILE: PluginDemo/framework.h ================================================ #pragma once #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 //// Windows 头文件 //#include #include #include // MFC 核心组件和标准组件 #include // MFC 扩展 #include // MFC 自动化类 #ifdef _UNICODE #if defined _M_IX86 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_X64 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") #else #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #endif #endif ================================================ FILE: PluginDemo/pch.cpp ================================================ // pch.cpp: 与预编译标头对应的源文件 #include "pch.h" // 当使用预编译的头时,需要使用此源文件,编译才能成功。 ================================================ FILE: PluginDemo/pch.h ================================================ // pch.h: 这是预编译标头文件。 // 下方列出的文件仅编译一次,提高了将来生成的生成性能。 // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 // 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 // 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 #ifndef PCH_H #define PCH_H // 添加要在此处预编译的标头 #include "framework.h" #include "resource.h" #endif //PCH_H ================================================ FILE: PluginDemo/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 PluginDemo.rc 使用 // #define IDD_DIALOG1 101 #define IDD_OPTIONS_DIALOG 101 #define IDS_PLUGIN_NAME 103 #define IDS_PLUGIN_DESCRIPTION 104 #define IDS_TIME 105 #define IDS_DATE 106 #define IDS_CUSTOM_DRAW_ITEM 107 #define IDC_SHOW_SECOND_CHECK 1001 #define IDC_SHOW_LABEL_CHECK 1002 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: README.md ================================================ **简体中文 | [English](./README_en-us.md)** [![Badge](https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg?style=flat-square)](https://996.icu/#/en_US) [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg?style=flat-square)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/zhongyang219/TrafficMonitor/main.yml?branch=master&label=Release%20CI&logo=github&style=flat-square)](https://github.com/zhongyang219/TrafficMonitor/actions?query=workflow:"Release+CI") [![GitHub release](https://img.shields.io/github/release/zhongyang219/TrafficMonitor.svg?style=flat-square)](https://github.com/zhongyang219/TrafficMonitor/releases/latest) Featured|HelloGitHub # TrafficMonitor 简介 Traffic Monitor是一款用于Windows平台的网速监控悬浮窗软件,可以显示当前网速、CPU及内存利用率,支持嵌入到任务栏显示,支持更换皮肤、历史流量统计等功能。 # 相关链接: 请[点击此处](https://github.com/zhongyang219/TrafficMonitor/releases/latest)下载TrafficMonitor的最新版本。 备用链接:[百度网盘下载](https://pan.baidu.com/s/15PMt7s-ASpyDwtS__4cUhg) 提取码:`ou0m` 国内用户如果遇到Github下载缓慢的问题,可以[点击此处](https://gitee.com/zhongyang219/TrafficMonitor)转到此项目在Gitee上的页面。 如果遇到问题,请[点击此处](./Help.md)查看常见问题。 你也可以[点击此处](https://github.com/zhongyang219/TrafficMonitor/actions?query=workflow:"Release+CI")下载TrafficMonitor的预发行构建版本。 从1.80版本开始,TrafficMonitor加入了温度监控功能,如果你不需要温度监控功能,并且在使用1.80以上版本中遇到了问题,建议下载不含温度监控的版本(Lite版本)。(在Release页面找到文件名包含`Lite`的版本。) TrafficMonitor依赖于Microsoft Visual C++ 运行环境,如果程序启动时提示“找不到MSVC*.dll”,请点击以下链接下载并安装Microsoft Visual C++ 运行环境。 [最新支持的 Visual C++ 可再发行程序包下载 | Microsoft Docs](https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist?view=msvc-170) # 版本说明 TrafficMonitor提供了标准版和Lite版两种版本可用。标准版包含了所有的功能,Lite版本则不包含温度监控、显卡利用率、硬盘利用率等硬件监控功能。标准版运行需要管理员权限,而Lite版本则不需要。 如果没有监控温度等硬件信息的需要,建议使用Lite版。 以下是两个版本功能对比。 | 功能 | 标准版 | Lite版 | | ----------------------------- | ------ | ------ | | 网速监控 | ✔ | ✔ | | CPU、内存利用率 | ✔ | ✔ | | CPU、显卡、硬盘、主板温度监控 | ✔ | ❌ | | CPU频率监控 | ✔ | ✔ | | 显卡利用率监控 | ✔ | ❌ | | 硬盘利用率监控 | ✔ | ❌ | | 网络详细信息 | ✔ | ✔ | | 插件系统 | ✔ | ✔ | | 主窗口更换皮肤 | ✔ | ✔ | | 需要管理员权限 | 是 | 否 | # 主要特性 * 显示当前实现网络传输速率、CPU和内存占用率 * 如果电脑有多个网卡,支持自动和手动选择网络连接 * 查看网络详细信息 * 支持嵌入到任务栏显示 * 支持更换皮肤和自定义皮肤 * 历史流量统计 * 硬件信息监控 * 插件系统 # 使用说明 **[点击这里](https://github.com/zhongyang219/TrafficMonitor/wiki)转到Wiki页面查看关于TrafficMonitor的详细说明文档。** # 截图 主悬浮窗: ![](./Screenshots/main1.png) 右键菜单: ![](./Screenshots/main.png) 任务栏窗口: ![](./Screenshots/taskbar.PNG) 多彩皮肤: # 如何使用 程序启动后在会在屏幕中显示一个显示网速的悬浮窗。在悬浮窗上点击鼠标右键可以弹出右键菜单。 TrafficMonitor支持将信息显示到任务栏。但是TrafficMonitor默认只显示主窗口(悬浮窗),如果需要让它嵌入到任务栏显示,请在右键菜单中选择“显示任务栏窗口”命令。 任务栏窗口支持自定义显示项目,默认情况下只显示网速,如果需要显示CPU和内存利用率等其他信息,请在任务栏窗口右键菜单中选择“显示设置”,在弹出的“显示设置”对话框中勾选需要显示的项目,如下图所示: # 自定义皮肤 在主窗口或通知区图标右键菜单上选择“其他功能”——“更换皮肤”可以打开更换皮肤界面。[点击此处](https://github.com/zhongyang219/TrafficMonitorSkin/blob/master/皮肤下载.md)可以下载更多皮肤。用户还可以根据自己的需要编辑自己的皮肤。 皮肤文件放在程序所在目录的`skins`目录下,每个皮肤被放到单独的文件夹下,文件夹的名称就是皮肤的名称。 其中`background.bmp`和`background_l.bmp`是背景图片,`skin.ini`是皮肤的配置文件,可以通过`skin.ini`指定文本颜色、字体、皮肤作者、每个项目的大小和位置等信息。 从1.80版本开始增加了xml格式的皮肤配置文件`skin.xml`,只有xml格式的皮肤配置文件才支持温度和显卡使用率显示。 从1.85版本开始增加了对png格式背景图片的支持,你可以使用png格式来制作带透明背景的皮肤,背景图片的文件名为`background.png`和`background_l.png`。 详细的皮肤制作教程请点击以下链接: [皮肤制作教程 · zhongyang219/TrafficMonitor Wiki (github.com)](https://github.com/zhongyang219/TrafficMonitor/wiki/皮肤制作教程) # 选项设置 在右键菜单选择“选项...”可以进入选项设置。在选项设置对话框中,可以单独设置主窗口和任务栏窗口的文本颜色、字体、背景颜色、网速单位、显示的文本等。 在“常规设置”选项卡中,可以设置是否在程序时自动检查更新,以及是否需要在开机是自动运行。可以设置在什么时候需要发出消息通知。 从1.72版本开始,支持每个项目文本颜色单独设置。勾选“指定每个项目的颜色”后,点击“文本颜色”右边的颜色框,会弹出详细颜色设置的对话框,可以在这里单独指定每个项目的颜色。 # 插件系统 从1.82版本开始增加了插件系统,插件dll必须放在“TrafficMonitor.exe”同级目录的“plugins”目录下。程序启动后,插件会自动加载。你可以在右键菜单“更多功能”——“插件管理”中查看并管理已加载的插件。 关于如何开发TrafficMonitor的说明,请参见[插件开发指南 · zhongyang219/TrafficMonitor Wiki (github.com)](https://github.com/zhongyang219/TrafficMonitor/wiki/插件开发指南)。 要下载TrafficMonitor插件,请[点击这里](https://github.com/zhongyang219/TrafficMonitorPlugins/blob/main/download/plugin_download.md)。 # 关于硬件监控功能 从1.80版本开始,TrafficMonitor加入了硬件监控功能(包括温度监控和显卡使用率监控、CPU频率监控),它使用了第三方开源库[LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor)。如果你在使用温度监控功能时遇到了问题,请[点击这里](./Help.md#13-关于trafficmonitor温度监控的问题)。 需要注意的是,温度监控功能默认是关闭的,如果你要使用TrafficMonitor的温度监控功能,请到[“选项设置”-“常规设置”-“硬件监控”](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#硬件监控)中开启。 **注意:硬件监控功能(包括温度监控和显卡使用率监控)可能存在一些问题,它可能会占用更多的CPU和内存。据部分用户反馈,开启温度功能后会导致程序崩溃和系统死机等问题,请在知晓以上风险后再决定开启硬件监控功能。否则,请不要使用硬件监控功能。** # 更新日志 **[点击此处查看更新日志](./UpdateLog/update_log.md)** ================================================ FILE: README_en-us.md ================================================ **[简体中文](./README.md) | English** [![Badge](https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg?style=flat-square)](https://996.icu/#/en_US) [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg?style=flat-square)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/zhongyang219/TrafficMonitor/main.yml?branch=master&label=Release%20CI&logo=github&style=flat-square)](https://github.com/zhongyang219/TrafficMonitor/actions?query=workflow:"Release+CI") [![GitHub release](https://img.shields.io/github/release/zhongyang219/TrafficMonitor.svg?style=flat-square)](https://github.com/zhongyang219/TrafficMonitor/releases/latest) Featured|HelloGitHub # TrafficMonitor Introduction TrafficMonitor is a network monitoring software with floating window feature for Windows. It displays the current internet speed and CPU and RAM usage. There are also other capabilities like an embedded display in the taksbar, changeable display skins, and historical traffic statistics. # Related Links Please [click here](https://github.com/zhongyang219/TrafficMonitor/releases/latest) to download the latest version of TrafficMonitor. Alternate link: Download from [Baidu Netdisk](https://pan.baidu.com/s/15PMt7s-ASpyDwtS__4cUhg). Access code: `ou0m` You can find the project page on Gitee by [clicking here](https://gitee.com/zhongyang219/TrafficMonitor) If you encounter any problems, please [click here](./Help_en-us.md) for frequently asked questions. You can also [click here](https://github.com/zhongyang219/TrafficMonitor/actions?query=workflow:"Release+CI") to download the pre-release build version of TrafficMonitor. For version 1.80 and later, the temperature monitoring function has been added. If the user does not need the temperature monitoring function and encounters problems with version 1.80 or later, it is recommended to download the earlier version without the temperature monitor (Lite version). (You can find the `Lite` version on the Release page.) TrafficMonitor relies on the Microsoft Visual C++ operrating environment. If an error prompts "Cannot find MSVC*.dll" when the program starts, please click the link below to download and install the Microsoft Visual C++ operating environment. [Download the latest supported version of Visual C++ Redistributable Package | Microsoft Docs](https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) # Version description There are two versions of TrafficMonitor, the standard version and the Lite version. The standard version includes all the functions, while the Lite version does not include hardware monitoring functions such as temperature monitoring, GPU usage, and hard disk usage. The standard version requires administrator privilege to run, while the Lite version does not. If there is no need to monitor the temperature and other hardware information, it is recommended to use the Lite version. See below for the functions comparison of the two versions. | Functions | Standard Version | Lite Version | | --------------------------------------------- | ---------------- | ------------ | | Internet speed monitoring | ✔ | ✔ | | CPU and RAM usage monitoring | ✔ | ✔ | | CPU, GPU, hard disk, mother board temperature | ✔ | ❌ | | CPU frequency monitoring | ✔ | ✔ | | GPU usage monitoring | ✔ | ❌ | | Hard disk usage monitoring | ✔ | ❌ | | Network connection details | ✔ | ✔ | | Plug-in system | ✔ | ✔ | | Change the skin of the main window | ✔ | ✔ | | Administrator privilege required | Yes | No | # Main Features * Displays current network transmission speed and CPU and RAM usage * If multiple network adapters are available, users can automatically or manually select networks to connect * Display network connection details * Support embedded display in taskbar * Support skin change and customizable skins * Historical traffic statistics * Hardware information monitoring * Plug-in system # Instructions for use **[Click here](https://github.com/zhongyang219/TrafficMonitor/wiki) to go to the Wiki page to view detailed documentation on TrafficMonitor (Only Available in Chinese).** # Screenshots Main Floating Window: ![](./Screenshots/en_us/main1.png) Right-Click Menu: ![](./Screenshots/en_us/main.png) Taskbar Window ![](./Screenshots/en_us/taskbar.png) Colorful Skins: ![](./Screenshots/skins.PNG) Change Skins: ![](./Screenshots/en_us/selecte_skin.png) Options: # How to Use As the program starts, a floating window showing network speed will appear on the screen. Right click on the floating window to open up the pop-up menu. TrafficMonitor can display information on the taskbar. However, the default setting for TrafficMonitor shows only the main window (floating window), in order to show embedded display on the taskbar, right click the window and select "Show Taskbar Window" in the pop-up menu. Users can customize displayed items on the embedded taskbar window. By default, only the network speed is displayed. In order to display CPU and RAM usage, or other informations, please select “Display Settings” in the right-click menu of the taskbar window, and check the items you want to display in the “Display Settings” dialog box, as shown below: # Customizable Skin You can select “Other Functions”-“Change Skin” on the right-click menu of the main window or notification area icon to open the interface of changing skin. Users can also download more skins and customize the skins [here](https://github.com/zhongyang219/TrafficMonitorSkin/blob/master/皮肤下载.md) according to their own needs. Skin files are stored under the `skins` directory within the directory where the app is located. Each skin is stored in its individual folder and the name of the folder is the name of the skin. Among the files, `background.bmp` and `background_l.bmp` are background pictures,`skin.ini` is the configuration document for the skin, users can customize text colors, fonts, skin creator, and the size and location of each items etc. Since version 1.80, an xml format of skin configuration file `skin.xml` has been added. Without the xml skin configuration document, TrafficMonitor will not be able to display temperature and GUP usage. Since version 1.85, support for background images in png format has been added, you can use the png format to create skins with transparent backgrounds. The file names of the background image files are `background.png` and `background_l.png`. Click the link below for detailed instruction on skin making: [皮肤制作教程 · zhongyang219/TrafficMonitor Wiki (github.com)](https://github.com/zhongyang219/TrafficMonitor/wiki/皮肤制作教程) # Configuring Options
Choosing "Options..." in the pop-up menu allows users to access the options configuration. In the dialog box of options configuration, the user can set the text color, text font, background color, net speed unit, and displayed text etc. for the main window and task window separately. In "Regular Configurations", users can change the settings on whether the program conducts auto-update and whether the program runs automatically when the computer starts up. Users can also configure when notifications need to be sent. For version 1.72 and later, users can configure text colors for each item individually. Check the option to "Designate Colors for Each Items", click the color box on the right of "text colors" to prompt a pop-up dialog box for detailed color configuration where users can designate colors for each items inidividually. # Plug-in system For version 1.82 and later, plug-in system has been added. The plug-in dll must be placed in the "plugins" directory, at the same level directory with "TrafficMonitor.exe". The plug-in should load automatically as the program starts up. Users can view and manage the loaded plugins in the pop-up menu "More Functions"-"Plugin Management". For instructions on how to develop the plugins for TrafficMonitor, please see [Plugin Development Guide · zhongyang219/TrafficMonitor Wiki (github.com)](https://github.com/zhongyang219/TrafficMonitor/wiki/Plugin-Development-Guide). To download the TrafficMonitor plugins, please [click here](https://github.com/zhongyang219/TrafficMonitorPlugins/blob/main/download/plugin_download.md). # About the hardware monitoring function For version 1.80 or later, the hardware monitoring functions (including temperature, CPU frequency, and GPU usage monitoring) have been added to TrafficMonitor. It relies on the open source library [LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor). If you encounter issues using the temperature monitoring function, please [click here](./Help_en-us.md#13-about-the-temperature-monitoring-of-trafficmonitor). It should also be noted that the temperature monitoring function is turned off by default. If users want to use the temperature monitoring function in TrafficMonitor, please go to ["Option Settings"-"General Settings"-"Hardware Monitoring"](https://github.com/zhongyang219/TrafficMonitor/wiki/选项设置#硬件监控) to enable it. **Note: The hardware monitoring function (including temperature monitoring and GPU usage monitoring) may still have some issues, which might consume more CPU and RAM. According to the feedback from some users, turning on the temperature function causes issues such as program crashing and system crashing, etc. Please consider the above risks before turning on the hardware monitoring function. Otherwise, please do not use the hardware monitoring function.** # Update log **[Click here to view the update log.](./UpdateLog/update_log_en-us.md)** ================================================ FILE: TrafficMonitor/AboutDlg.cpp ================================================ #include "stdafx.h" #include "TrafficMonitor.h" #include "AboutDlg.h" #include "MessageDlg.h" #include "DrawCommon.h" BEGIN_MESSAGE_MAP(CAboutDlg, CBaseDialog) //ON_STN_CLICKED(IDC_STATIC_DONATE, &CAboutDlg::OnStnClickedStaticDonate) ON_MESSAGE(WM_LINK_CLICKED, &CAboutDlg::OnLinkClicked) ON_WM_PAINT() ON_WM_ERASEBKGND() ON_WM_CTLCOLOR() END_MESSAGE_MAP() CAboutDlg::CAboutDlg() : CBaseDialog(IDD_ABOUTBOX) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_STATIC_MAIL, m_mail); DDX_Control(pDX, IDC_STATIC_ACKNOWLEDGEMENT, m_acknowledgement); DDX_Control(pDX, IDC_STATIC_GITHUB, m_github); DDX_Control(pDX, IDC_TRANSLATOR_STATIC, m_translator_static); DDX_Control(pDX, IDC_STATIC_LICENSE, m_license); DDX_Control(pDX, IDC_OPENHARDWAREMONITOR_LINK, m_openhardwaremonitor_link); DDX_Control(pDX, IDC_TINYXML2_LINK, m_tinyxml2_link); DDX_Control(pDX, IDC_MUSICPLAYER2_LINK, m_musicplayer2_link); DDX_Control(pDX, IDC_SIMPLENOTEPAD_LINK, m_simplenotepad_link); DDX_Control(pDX, IDC_STATIC_GITEE, m_gitee); } CString CAboutDlg::GetDonateList() { return CCommon::GetTextResource(IDR_ACKNOWLEDGEMENT_TEXT, 2); } CString CAboutDlg::GetDialogName() const { return _T("AboutDlg"); } bool CAboutDlg::InitializeControls() { RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_STATIC_MAIL }, { CtrlTextInfo::L3, IDC_STATIC_GITHUB }, { CtrlTextInfo::L2, IDC_STATIC_GITEE }, { CtrlTextInfo::L4, IDC_STATIC_LICENSE }, { CtrlTextInfo::L3, IDC_STATIC_DONATE }, { CtrlTextInfo::L2, IDC_STATIC_ACKNOWLEDGEMENT } }); return true; } CRect CAboutDlg::CalculatePicRect() { CRect rect; GetClientRect(rect); CRect rc_pic = rect; ::GetWindowRect(GetDlgItem(IDC_STATIC_VERSION)->GetSafeHwnd(), rect); ScreenToClient(rect); rc_pic.bottom = rect.top - theApp.DPI(6); if (rc_pic.Height() <= 0) rc_pic.bottom = rc_pic.top + theApp.DPI(50); return rc_pic; } BOOL CAboutDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetRememberDlgSize(false); m_mail.SetURL(_T("mailto:zhongyang219@hotmail.com")); //设置超链接 //m_check_update.SetURL(_T("http://pan.baidu.com/s/1c1LkPQ4")); m_github.SetURL(_T("https://github.com/zhongyang219/TrafficMonitor")); m_gitee.SetURL(_T("https://gitee.com/zhongyang219/TrafficMonitor")); m_acknowledgement.SetLinkIsURL(false); m_license.SetLinkIsURL(false); m_openhardwaremonitor_link.SetURL(_T("https://github.com/LibreHardwareMonitor/LibreHardwareMonitor")); m_tinyxml2_link.SetURL(_T("https://github.com/leethomason/tinyxml2")); m_musicplayer2_link.SetURL(_T("https://github.com/zhongyang219/MusicPlayer2")); m_simplenotepad_link.SetURL(_T("https://github.com/zhongyang219/SimpleNotePad")); m_openhardwaremonitor_link.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_tinyxml2_link.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_musicplayer2_link.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_simplenotepad_link.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_mail.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_acknowledgement.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_github.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_gitee.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); m_license.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); //设置版本信息 CString version_info; GetDlgItemText(IDC_STATIC_VERSION, version_info); CString str_lite; #ifdef WITHOUT_TEMPERATURE str_lite = CCommon::LoadText(_T(" ("), IDS_WITHOUT_TEMPERATURE, _T(")")); #endif version_info = CCommon::StringFormat(version_info, { str_lite, VERSION }); #ifdef COMPILE_FOR_WINXP version_info += _T(" (For WinXP)"); #endif // COMPILE_FOR_WINXP #ifdef _M_ARM64EC version_info += _T(" (Arm64EC)"); #elif _M_X64 version_info += _T(" (x64)"); #endif #ifdef _DEBUG version_info += _T(" (Debug)"); #endif SetDlgItemText(IDC_STATIC_VERSION, version_info); //设置最后编译日期 CString temp_str; GetDlgItemText(IDC_STATIC_COPYRIGHT, temp_str); CString str_compile_time = CCommon::GetLastCompileTime(); temp_str.Replace(_T(""), str_compile_time); SetDlgItemText(IDC_STATIC_COPYRIGHT, temp_str); m_tool_tip.Create(this, TTS_ALWAYSTIP | TTS_NOPREFIX); m_tool_tip.AddTool(&m_mail, CCommon::LoadText(IDS_SEND_EMAIL_TO_ATHOUR, _T("\r\nmailto:zhongyang219@hotmail.com"))); //m_tool_tip.AddTool(&m_check_update, _T("到百度网盘链接查看是否有更新\r\nhttp://pan.baidu.com/s/1c1LkPQ4")); m_tool_tip.AddTool(&m_github, CCommon::LoadText(IDS_GOTO_GITHUB, _T("\r\nhttps://github.com/zhongyang219/TrafficMonitor"))); m_tool_tip.AddTool(&m_gitee, CCommon::LoadText(IDS_GOTO_GITEE, _T("\r\nhttps://gitee.com/zhongyang219/TrafficMonitor"))); m_tool_tip.AddTool(&m_openhardwaremonitor_link, m_openhardwaremonitor_link.GetURL()); m_tool_tip.AddTool(&m_tinyxml2_link, m_tinyxml2_link.GetURL()); m_tool_tip.AddTool(&m_musicplayer2_link, CCommon::LoadText(IDS_MUSICPLAYER2_DESCRIPTION) + _T("\r\n") + m_musicplayer2_link.GetURL()); m_tool_tip.AddTool(&m_simplenotepad_link, CCommon::LoadText(IDS_SIMPLENOTEPAD_DESCRIPTION) + _T("\r\n") + m_simplenotepad_link.GetURL()); m_tool_tip.SetDelayTime(300); //设置延迟 m_tool_tip.SetMaxTipWidth(800); //设置翻译者信息 const auto& language_info{ theApp.m_str_table.GetLanguageInfo() }; wstring language_tag{ language_info.bcp_47 }; if (language_info.translator.empty()) //没有翻译者时不显示翻译者信息 { m_translator_static.ShowWindow(SW_HIDE); } m_translator_static.SetWindowTextW(theApp.m_str_table.LoadTextFormat(L"TXT_ABOUT_TRANSLATOR", { language_info.display_name, language_info.translator }).c_str()); std::wstring translator_url{ language_info.translator_url }; if (!translator_url.empty()) //显示翻译者的信息 { //如果url中包含“@”但是前面没有“mailto:”,则在前面加上“mailto:” if (translator_url.find(L'@') != std::wstring::npos && (translator_url.size() < 7 || translator_url.substr(0, 7) != L"mailto:")) translator_url = L"mailto:" + translator_url; m_translator_static.SetURL(translator_url.c_str()); CString str_tool_tip = CCommon::LoadText(IDS_CONTACT_TRANSLATOR); str_tool_tip += _T("\r\n"); str_tool_tip += translator_url.c_str(); m_tool_tip.AddTool(&m_translator_static, str_tool_tip); } m_translator_static.SetBackgroundColor(GetSysColor(COLOR_WINDOW)); //加载图片 m_about_pic.LoadBitmap(IDB_ABOUT_BACKGROUND_HD); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } BOOL CAboutDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_MOUSEMOVE) m_tool_tip.RelayEvent(pMsg); return CBaseDialog::PreTranslateMessage(pMsg); } //void CAboutDlg::OnStnClickedStaticDonate() //{ // CDonateDlg donateDlg; // donateDlg.DoModal(); //} afx_msg LRESULT CAboutDlg::OnLinkClicked(WPARAM wParam, LPARAM lParam) { CWnd* pCtrl = (CWnd*)wParam; if (pCtrl == &m_acknowledgement) { CString strContent = GetDonateList(); //strContent += _T("\r\n"); //strContent += CCommon::LoadText(IDS_ACKNOWLEDGEMENT_EXPLAIN); CMessageDlg dlg; dlg.SetWindowTitle(CCommon::LoadText(IDS_TITLE_ACKNOWLEDGEMENT)); //dlg.SetInfoText(CCommon::LoadText(IDS_THANKS_DONORS)); dlg.SetMessageText(strContent); dlg.DoModal(); } else if (pCtrl == &m_license) { CMessageDlg dlg; dlg.SetWindowTitle(CCommon::LoadText(IDS_LICENSE)); dlg.SetInfoText(CCommon::LoadText(IDS_LICENSE_EXPLAIN)); dlg.SetMessageText(CCommon::GetTextResource(IDR_LICENSE, 1)); dlg.DoModal(); } return 0; } void CAboutDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CBaseDialog::OnPaint() CDrawCommon draw; draw.Create(&dc, this); CRect rc_pic = CalculatePicRect(); draw.GetDC()->FillSolidRect(rc_pic, RGB(161, 200, 255)); draw.DrawBitmap(m_about_pic, rc_pic.TopLeft(), rc_pic.Size(), CDrawCommon::StretchMode::FIT); } BOOL CAboutDlg::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CRect draw_rect; GetClientRect(draw_rect); pDC->FillSolidRect(draw_rect, GetSysColor(COLOR_WINDOW)); //绘制白色背景 int white_height; //白色区域的高度 CRect rc_copyright{}; ::GetWindowRect(GetDlgItem(IDOK)->GetSafeHwnd(), rc_copyright); ScreenToClient(rc_copyright); white_height = rc_copyright.top - theApp.DPI(6); //绘制“确定”按钮上方的分割线 CRect rc_line{ draw_rect }; rc_line.top = white_height; rc_line.bottom = white_height + theApp.DPI(1); pDC->FillSolidRect(rc_line, RGB(210, 210, 210)); //绘制灰色背景 CRect rc_gray{ rc_line }; rc_gray.top = rc_line.bottom; rc_gray.bottom = draw_rect.bottom; pDC->FillSolidRect(rc_gray, GetSysColor(COLOR_BTNFACE)); return TRUE; //return CBaseDialog::OnEraseBkgnd(pDC); } HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CBaseDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 //去掉static控件的灰色灰色背景 UINT ctrl_id = pWnd->GetDlgCtrlID(); if (ctrl_id == IDC_STATIC_VERSION || ctrl_id == IDC_STATIC_COPYRIGHT || ctrl_id == IDC_STATIC) { static HBRUSH hBackBrush{}; if (hBackBrush == NULL) hBackBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); pDC->SetBkColor(GetSysColor(COLOR_WINDOW)); return hBackBrush; } // TODO: 如果默认的不是所需画笔,则返回另一个画笔 return hbr; } ================================================ FILE: TrafficMonitor/AboutDlg.h ================================================ #pragma once #include "LinkStatic.h" #include "BaseDialog.h" // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CBaseDialog { public: CAboutDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: CLinkStatic m_mail; //“联系作者”超链接 CLinkStatic m_acknowledgement; //“鸣谢”超链接 CLinkStatic m_github; //“GitHub”超链接 CLinkStatic m_gitee; //“Gitee”超链接 CLinkStatic m_license; //“开源协议”超链接 CToolTipCtrl m_tool_tip; //鼠标指向时的工具提示 CLinkStatic m_translator_static; CLinkStatic m_openhardwaremonitor_link; CLinkStatic m_tinyxml2_link; CLinkStatic m_musicplayer2_link; CLinkStatic m_simplenotepad_link; CBitmap m_about_pic; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 CString GetDonateList(); //从资源文件加载捐赠人员名单 virtual CString GetDialogName() const override; virtual bool InitializeControls() override; CRect CalculatePicRect(); //计算图片的位置 // 实现 protected: DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); virtual BOOL PreTranslateMessage(MSG* pMsg); // afx_msg void OnStnClickedStaticDonate(); protected: afx_msg LRESULT OnLinkClicked(WPARAM wParam, LPARAM lParam); public: afx_msg void OnPaint(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); }; ================================================ FILE: TrafficMonitor/AdapterCommon.cpp ================================================ #include "stdafx.h" #include "AdapterCommon.h" CAdapterCommon::CAdapterCommon() { } CAdapterCommon::~CAdapterCommon() { } void CAdapterCommon::GetAdapterInfo(vector& adapters) { adapters.clear(); PIP_ADAPTER_INFO pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[sizeof(IP_ADAPTER_INFO)]; //PIP_ADAPTER_INFO结构体指针存储本机网卡信息 unsigned long stSize = sizeof(IP_ADAPTER_INFO); //得到结构体大小,用于GetAdaptersInfo参数 int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize); //调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量;其中stSize参数既是一个输入量也是一个输出量 if (ERROR_BUFFER_OVERFLOW == nRel) { //如果函数返回的是ERROR_BUFFER_OVERFLOW //则说明GetAdaptersInfo参数传递的内存空间不够,同时其传出stSize,表示需要的空间大小 //这也是说明为什么stSize既是一个输入量也是一个输出量 delete[] (BYTE*)pIpAdapterInfo; //释放原来的内存空间 pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize]; //重新申请内存空间用来存储所有网卡信息 nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize); //再次调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量 } PIP_ADAPTER_INFO pIpAdapterInfoHead = pIpAdapterInfo; //保存pIpAdapterInfo链表中第一个元素的地址 if (ERROR_SUCCESS == nRel) { while (pIpAdapterInfo) { NetWorkConection connection; connection.description = pIpAdapterInfo->Description; connection.ip_address = CCommon::StrToUnicode(pIpAdapterInfo->IpAddressList.IpAddress.String); connection.subnet_mask = CCommon::StrToUnicode(pIpAdapterInfo->IpAddressList.IpMask.String); connection.default_gateway = CCommon::StrToUnicode(pIpAdapterInfo->GatewayList.IpAddress.String); adapters.push_back(connection); pIpAdapterInfo = pIpAdapterInfo->Next; } } //释放内存空间 if (pIpAdapterInfoHead) { delete[] (BYTE*)pIpAdapterInfoHead; } if (adapters.empty()) { NetWorkConection connection{}; connection.description = CCommon::UnicodeToStr(CCommon::LoadText(L"<", IDS_NO_CONNECTION, L">")); adapters.push_back(connection); } } void CAdapterCommon::RefreshIpAddress(vector& adapters) { vector adapters_tmp; GetAdapterInfo(adapters_tmp); for (const auto& adapter_tmp : adapters_tmp) { for (auto& adapter : adapters) { if (adapter_tmp.description == adapter.description) { adapter.ip_address = adapter_tmp.ip_address; adapter.subnet_mask = adapter_tmp.subnet_mask; adapter.default_gateway = adapter_tmp.default_gateway; } } } } void CAdapterCommon::GetIfTableInfo(vector& adapters, MIB_IFTABLE* pIfTable) { //依次在IfTable里查找每个连接 for (size_t i{}; i < adapters.size(); i++) { if (adapters[i].description.empty()) continue; int index; index = FindConnectionInIfTable(adapters[i].description, pIfTable); if (index == -1) //如果使用精确匹配的方式没有找到,则采用模糊匹配的方式再查找一次 index = FindConnectionInIfTableFuzzy(adapters[i].description, pIfTable); //if (index != -1) //{ adapters[i].index = index; adapters[i].in_bytes = pIfTable->table[index].dwInOctets; adapters[i].out_bytes = pIfTable->table[index].dwOutOctets; adapters[i].description_2 = (const char*)pIfTable->table[index].bDescr; //} } } void CAdapterCommon::GetAllIfTableInfo(vector& adapters, MIB_IFTABLE * pIfTable) { vector adapters_tmp; GetAdapterInfo(adapters_tmp); //获取IP地址 adapters.clear(); for (size_t i{}; i < pIfTable->dwNumEntries; i++) { NetWorkConection connection; connection.description = connection.description_2 = (const char*)pIfTable->table[i].bDescr; connection.index = i; connection.in_bytes = pIfTable->table[i].dwInOctets; connection.out_bytes = pIfTable->table[i].dwOutOctets; for (size_t j{}; j < adapters_tmp.size(); j++) { if (connection.description.find(adapters_tmp[j].description) != string::npos) { connection.ip_address = adapters_tmp[j].ip_address; connection.subnet_mask = adapters_tmp[j].subnet_mask; connection.default_gateway = adapters_tmp[j].default_gateway; break; } } adapters.push_back(connection); } } int CAdapterCommon::FindConnectionInIfTable(string connection, MIB_IFTABLE* pIfTable) { for (size_t i{}; i < pIfTable->dwNumEntries; i++) { string descr = (const char*)pIfTable->table[i].bDescr; if (descr == connection) return i; } return -1; } int CAdapterCommon::FindConnectionInIfTableFuzzy(string connection, MIB_IFTABLE* pIfTable) { for (size_t i{}; i < pIfTable->dwNumEntries; i++) { string descr = (const char*)pIfTable->table[i].bDescr; size_t index; //在较长的字符串里查找较短的字符串 if (descr.size() >= connection.size()) index = descr.find(connection); else index = connection.find(descr); if (index != wstring::npos) return i; } //如果还是没有找到,则使用字符串匹配算法查找 double max_degree{}; int best_index{}; for (size_t i{}; i < pIfTable->dwNumEntries; i++) { string descr = (const char*)pIfTable->table[i].bDescr; double degree = CCommon::StringSimilarDegree_LD(descr, connection); if (degree > max_degree) { max_degree = degree; best_index = i; } } return best_index; } ================================================ FILE: TrafficMonitor/AdapterCommon.h ================================================ #pragma once #include "Common.h" //һϢ struct NetWorkConection { int index{}; //MIB_IFTABLEе string description; //ȡGetAdapterInfo string description_2; //ȡGetIfTable unsigned int in_bytes; //ʼʱѽֽ unsigned int out_bytes; //ʼʱѷֽ wstring ip_address{ L"-.-.-.-" }; //IPַ wstring subnet_mask{ L"-.-.-.-" }; // wstring default_gateway{ L"-.-.-.-" }; //Ĭ }; class CAdapterCommon { public: CAdapterCommon(); ~CAdapterCommon(); //ȡбIPַ롢ĬϢ static void GetAdapterInfo(vector& adapters); //ˢбеIPַ롢ĬϢ static void RefreshIpAddress(vector& adapters); //ȡбÿӵMIB_IFTABLEеʼʱѽ/ֽϢ static void GetIfTableInfo(vector& adapters, MIB_IFTABLE* pIfTable); //ֱӽMIB_IFTABLEеӵadapters static void GetAllIfTableInfo(vector& adapters, MIB_IFTABLE* pIfTable); private: //һжǷIfTableбҲ򷵻-1 static int FindConnectionInIfTable(string connection, MIB_IFTABLE* pIfTable); //һжǷIfTableбҲ򷵻-1ֻҪƥ static int FindConnectionInIfTableFuzzy(string connection, MIB_IFTABLE* pIfTable); }; ================================================ FILE: TrafficMonitor/AppAlreadyRuningDlg.cpp ================================================ // AppAlreadyRuningDlg.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "AppAlreadyRuningDlg.h" #include "afxdialogex.h" // CAppAlreadyRuningDlg 对话框 IMPLEMENT_DYNAMIC(CAppAlreadyRuningDlg, CBaseDialog) CAppAlreadyRuningDlg::CAppAlreadyRuningDlg(HWND handel, CWnd* pParent /*=nullptr*/) : CBaseDialog(IDD_APP_ALREAD_RUNING_DIALOG, pParent), m_handle(handel) { } CAppAlreadyRuningDlg::~CAppAlreadyRuningDlg() { } void CAppAlreadyRuningDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); } CString CAppAlreadyRuningDlg::GetDialogName() const { return _T("AppAlreadyRuningDlg"); } BEGIN_MESSAGE_MAP(CAppAlreadyRuningDlg, CBaseDialog) ON_BN_CLICKED(IDC_EXIT_INST_BUTTON, &CAppAlreadyRuningDlg::OnBnClickedExitInstButton) ON_BN_CLICKED(IDC_OPEN_SETTINGS_BUTTON, &CAppAlreadyRuningDlg::OnBnClickedOpenSettingsButton) ON_BN_CLICKED(IDC_SHOW_HIDE_MAIN_WINDOW_BUTTON, &CAppAlreadyRuningDlg::OnBnClickedShowHideMainWindowButton) ON_BN_CLICKED(IDC_SHOW_HIDE_TASKBAR_WINDOW_BUTTON, &CAppAlreadyRuningDlg::OnBnClickedShowHideTaskbarWindowButton) END_MESSAGE_MAP() // CAppAlreadyRuningDlg 消息处理程序 BOOL CAppAlreadyRuningDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CAppAlreadyRuningDlg::OnBnClickedExitInstButton() { ::PostMessage(m_handle, WM_COMMAND, ID_APP_EXIT, 0); } void CAppAlreadyRuningDlg::OnBnClickedOpenSettingsButton() { ::PostMessage(m_handle, WM_COMMAND, ID_OPTIONS, 0); } void CAppAlreadyRuningDlg::OnBnClickedShowHideMainWindowButton() { ::PostMessage(m_handle, WM_COMMAND, ID_SHOW_MAIN_WND, 0); } void CAppAlreadyRuningDlg::OnBnClickedShowHideTaskbarWindowButton() { ::PostMessage(m_handle, WM_COMMAND, ID_SHOW_TASK_BAR_WND, 0); } ================================================ FILE: TrafficMonitor/AppAlreadyRuningDlg.h ================================================ #pragma once #include "BaseDialog.h" // CAppAlreadyRuningDlg 对话框 class CAppAlreadyRuningDlg : public CBaseDialog { DECLARE_DYNAMIC(CAppAlreadyRuningDlg) public: CAppAlreadyRuningDlg(HWND handel, CWnd* pParent = nullptr); // 标准构造函数 virtual ~CAppAlreadyRuningDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_APP_ALREAD_RUNING_DIALOG }; #endif private: HWND m_handle{}; //正在运行的TrafficMonitor进程主窗口的句柄 protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 virtual CString GetDialogName() const override; DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnBnClickedExitInstButton(); afx_msg void OnBnClickedOpenSettingsButton(); afx_msg void OnBnClickedShowHideMainWindowButton(); afx_msg void OnBnClickedShowHideTaskbarWindowButton(); }; ================================================ FILE: TrafficMonitor/BaseDialog.cpp ================================================ // BaseDialog.cpp : 实现文件 // #include "stdafx.h" #include "BaseDialog.h" #include "afxdialogex.h" #include "IniHelper.h" #include "TrafficMonitor.h" #include "TrafficMonitorDlg.h" // CBaseDialog 对话框 std::map CBaseDialog::m_unique_hwnd; IMPLEMENT_DYNAMIC(CBaseDialog, CDialog) CBaseDialog::CBaseDialog(UINT nIDTemplate, CWnd* pParent /*=NULL*/) : CDialog(nIDTemplate, pParent) { m_nDialogID = nIDTemplate; m_dpi = theApp.GetDpi(); } CBaseDialog::~CBaseDialog() { } void CBaseDialog::SetBackgroundColor(COLORREF color, BOOL bRepaint) { if (m_brBkgr.GetSafeHandle() != NULL) { m_brBkgr.DeleteObject(); } if (color != (COLORREF)-1) { m_brBkgr.CreateSolidBrush(color); } if (bRepaint && GetSafeHwnd() != NULL) { Invalidate(); UpdateWindow(); } } void CBaseDialog::SetMinSize(int cx, int cy) { m_min_size.cx = cx; m_min_size.cy = cy; } HWND CBaseDialog::GetUniqueHandel(LPCTSTR dlg_name) { return m_unique_hwnd[dlg_name]; } const std::map& CBaseDialog::AllUniqueHandels() { return m_unique_hwnd; } bool CBaseDialog::IsAllDialogClosed() { for (const auto& hwnd : m_unique_hwnd) { if (hwnd.second != nullptr) return false; } return true; } void CBaseDialog::LoadConfig() { if (!GetDialogName().IsEmpty() && m_remember_dlg_size) { CIniHelper ini{ theApp.m_config_path }; //载入窗口大小设置 m_window_size.cx = ini.GetInt(_T("window_size"), GetDialogName() + _T("_width"), -1); m_window_size.cy = ini.GetInt(_T("window_size"), GetDialogName() + _T("_height"), -1); } } void CBaseDialog::SaveConfig() const { if (!GetDialogName().IsEmpty() && m_remember_dlg_size) { CIniHelper ini{ theApp.m_config_path }; //保存窗口大小设置 ini.WriteInt(_T("window_size"), GetDialogName() + _T("_width"), m_window_size.cx); ini.WriteInt(_T("window_size"), GetDialogName() + _T("_height"), m_window_size.cy); ini.Save(); } } void CBaseDialog::IterateControls(CWnd* pParent, std::function func) { if (pParent == nullptr) return; // 获取第一个子控件 CWnd* pChild = pParent->GetWindow(GW_CHILD); // 遍历所有子控件 while (pChild != nullptr) { func(pChild); // 递归遍历子控件的子控件(处理嵌套控件) IterateControls(pChild, func); // 获取下一个兄弟控件 pChild = pChild->GetWindow(GW_HWNDNEXT); } } void CBaseDialog::ReLoadLayoutResource() { ASSERT(m_nDialogID); CMFCDynamicLayout* pDynamicLayout = GetDynamicLayout(); if (pDynamicLayout) { HRSRC layoutRes = ::FindResourceW(NULL, MAKEINTRESOURCEW(m_nDialogID), RT_DIALOG_LAYOUT); if (layoutRes) { HGLOBAL hResData = ::LoadResource(NULL, layoutRes); if (hResData) { LPVOID layoutResData = ::LockResource(hResData); DWORD layoutResSize = ::SizeofResource(NULL, layoutRes); // std::wstring data(static_cast(layoutResData), layoutResSize / sizeof(wchar_t)); pDynamicLayout->LoadResource(this, layoutResData, layoutResSize); } } } } int CBaseDialog::DPI(int pixel) const { return m_dpi * pixel / 96; } CRect CBaseDialog::GetTextExtent(const CString& text) { ASSERT(m_pDC != nullptr); // m_pDC由OnInitDialog负责申请释放 if (m_pDC == nullptr) return CRect(); if (text.IsEmpty()) return CRect(); CRect text_size; m_pDC->DrawTextW(text, &text_size, DT_CALCRECT); // 使用CDC::DrawTextW测量文本宽度(CDC::GetTextExtent是理论宽度,不准确) return text_size; } void CBaseDialog::RepositionTextBasedControls(const vector& items, CtrlTextInfo::Width center_min_width) { ASSERT(m_pDC != nullptr); // 此方法仅在InitializeControls期间可用 if (m_pDC == nullptr) return; int center_width = theApp.DPI(center_min_width); std::map> col_info; struct itemInfo { int col_index; CWnd* p; CRect rect; }; vector items_info; int center_left{ 0 }, center_right{ INT_MAX }; for (const auto& item : items) { ASSERT(item.col_index != CtrlTextInfo::UN_USE); ASSERT(item.id != 0); // 获取所有列所需dx,以及左贴靠元素的右边缘center_left,右贴靠元素的左边缘center_right CWnd* pItem = GetDlgItem(item.id); if (pItem == nullptr) continue; CRect rect{}; pItem->GetWindowRect(&rect); ScreenToClient(&rect); CString text; pItem->GetWindowTextW(text); int dx = GetTextExtent(text).Width() + theApp.DPI(item.ext_width) - rect.Width(); if (dx < 0) dx = 0; // 文字只增加控件宽度 if (col_info[item.col_index].first < dx) // 取此列元素中宽度增长最多的 col_info[item.col_index].first = dx; if (item.col_index < 0 && center_left < rect.right) center_left = rect.right; if (item.col_index > 0 && center_right > rect.left) center_right = rect.left; if (item.col_index == 0) // col_index为0的控件可能有多个 { if (center_left < rect.left) center_left = rect.left; if (center_right > rect.right) center_right = rect.right; } items_info.emplace_back(itemInfo{ item.col_index, pItem, std::move(rect) }); } if (center_right == INT_MAX) // 如果控件全部都是左贴靠的那么以窗口右边缘作为剩余空间的右边缘 { CRect dlg_rect{}; GetClientRect(&dlg_rect); center_right = dlg_rect.Width(); } // 此断言触发说明资源文件中的原始布局没有给中间控件/空闲空间留够宽度 ASSERT(center_right - center_left >= center_width); int dx_sum_left{}, dx_sum_right{}; // 因为同一col_index可以有多个控件&没有要求顺序所以控件的最终位置必须可以无状态的计算出来 for (auto& a : col_info) { if (a.first < 0) { a.second.second = dx_sum_left; // 存储此列之前控件的总dx,即此列控件的右移距离 dx_sum_left += a.second.first; } else if (a.first > 0) { a.second.second = dx_sum_right; dx_sum_right += a.second.first; } } float scale{ 1.0f }; if (center_right - center_left - dx_sum_left - dx_sum_right < center_width) { // ASSERT(false); // 现在加载的文本使此行的中间控件/空闲空间被挤压的太小 // 这需要重新设计窗口控件排布以适应当前翻译长度,这里先简单的把缺少的空间分摊给各dx scale = static_cast(center_right - center_left - center_width) / (dx_sum_left + dx_sum_right); dx_sum_left = static_cast(dx_sum_left * scale + 0.5f); dx_sum_right = static_cast(dx_sum_right * scale + 0.5f); } for (const auto& item : items_info) { const auto& rect = item.rect; int dx = static_cast(col_info[item.col_index].first * scale + 0.5f); int sum_dx = static_cast(col_info[item.col_index].second * scale + 0.5f); if (item.col_index < 0) item.p->SetWindowPos(nullptr, rect.left + sum_dx, rect.top, rect.Width() + dx, rect.Height(), SWP_NOZORDER); else if (item.col_index > 0) item.p->SetWindowPos(nullptr, rect.left + sum_dx - dx_sum_right, rect.top, rect.Width() + dx, rect.Height(), SWP_NOZORDER); else if (item.col_index == 0) item.p->SetWindowPos(nullptr, rect.left + dx_sum_left, rect.top, rect.Width() - dx_sum_left - dx_sum_right, rect.Height(), SWP_NOZORDER); } } void CBaseDialog::EnableDlgCtrl(UINT id, bool enable) { CWnd* pWnd = GetDlgItem(id); if (pWnd != nullptr) pWnd->EnableWindow(enable); } void CBaseDialog::SetButtonIcon(UINT id, HICON hIcon) { CWnd* dlgItem = GetDlgItem(id); CButton* btn = static_cast(dlgItem); if (btn != nullptr) btn->SetIcon(hIcon); } CRect CBaseDialog::GetControlRect(CWnd* pCtrl) { if (pCtrl != nullptr) { CRect rect; pCtrl->GetWindowRect(rect); ScreenToClient(rect); return rect; } return CRect(); } CRect CBaseDialog::GetControlRect(UINT id) { CWnd* pCtrl = GetDlgItem(id); return GetControlRect(pCtrl); } void CBaseDialog::IterateControls(std::function func) { func(this); IterateControls(this, func); } void CBaseDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CBaseDialog, CDialog) ON_WM_DESTROY() ON_WM_GETMINMAXINFO() ON_WM_SIZE() ON_WM_ERASEBKGND() ON_WM_CTLCOLOR() END_MESSAGE_MAP() // CBaseDialog 消息处理程序 BOOL CBaseDialog::OnInitDialog() { m_unique_hwnd[GetDialogName()] = m_hWnd; CDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 //初始化字体 CRect rect; GetWindowRect(rect); FontInfo font_info; font_info.name = theApp.m_str_table.GetLanguageInfo().default_font_name.c_str(); font_info.size = 9; UINT dpi_x{}, dpi_y{}; if (theApp.DPIFromRect(rect, &dpi_x, &dpi_y)) m_dpi = dpi_x; font_info.Create(m_dlg_font, m_dpi); //获取初始时窗口的大小 if (m_min_size.cx <= 0 || m_min_size.cy <= 0) { CRect rect; GetWindowRect(rect); m_min_size.cx = rect.Width() * 96 / theApp.GetDpi(); m_min_size.cy = rect.Height() * 96 / theApp.GetDpi(); } //载入设置 LoadConfig(); SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); //取消置顶 //处理对话框中的文本翻译 IterateControls([&](CWnd* pWnd) { //设置控件字体 if (m_dlg_font.GetSafeHandle() != NULL) pWnd->SetFont(&m_dlg_font); //获取控件文本 CString str; pWnd->GetWindowText(str); UINT id = pWnd->GetDlgCtrlID(); if (str.Left(4) == _T("TXT_")) { //设置控件文本 const std::wstring& str_translated = theApp.m_str_table.LoadText(str.GetString()); if (!str_translated.empty()) pWnd->SetWindowTextW(str_translated.c_str()); } //处理标准按钮 else { if (id == IDOK) pWnd->SetWindowTextW(theApp.m_str_table.LoadText(TXT_OK).c_str()); else if (id == IDCANCEL) pWnd->SetWindowTextW(theApp.m_str_table.LoadText(TXT_CANCEL).c_str()); else if (id == IDCLOSE) pWnd->SetWindowTextW(theApp.m_str_table.LoadText(TXT_CLOSE).c_str()); } }); // 在还原窗口大小之前(当前窗口状态与资源一致),派生类执行控件文本初始化及调整控件排布 // 与实际窗口大小相关的初始化(比如表格列宽)应在派生类的OnInitDialog进行 m_pDC = GetDC(); m_pDC->SelectObject(&m_dlg_font); bool rtn = InitializeControls(); ReleaseDC(m_pDC); m_pDC = nullptr; // 如果更改了控件排布那么应当返回true以向布局管理器应用控件调整(重新加载动态布局设置) if (rtn) ReLoadLayoutResource(); //初始化窗口大小 if (m_window_size.cx > 0 && m_window_size.cy > 0) { SetWindowPos(nullptr, 0, 0, m_window_size.cx, m_window_size.cy, SWP_NOZORDER | SWP_NOMOVE); } return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CBaseDialog::OnDestroy() { CDialog::OnDestroy(); // TODO: 在此处添加消息处理程序代码 m_unique_hwnd[GetDialogName()] = NULL; SaveConfig(); //当所有对话框关闭时重新设置主窗口置顶 CTrafficMonitorDlg* pDlg = dynamic_cast(theApp.m_pMainWnd); if (pDlg != nullptr && IsAllDialogClosed()) pDlg->SetAlwaysOnTop(); } void CBaseDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //限制窗口最小大小 lpMMI->ptMinTrackSize.x = theApp.DPI(m_min_size.cx); //设置最小宽度 lpMMI->ptMinTrackSize.y = theApp.DPI(m_min_size.cy); //设置最小高度 CDialog::OnGetMinMaxInfo(lpMMI); } void CBaseDialog::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 if (nType != SIZE_MAXIMIZED && nType != SIZE_MINIMIZED) { //m_window_width = cx; //m_window_hight = cy; CRect rect; GetWindowRect(&rect); m_window_size.cx = rect.Width(); m_window_size.cy = rect.Height(); } } INT_PTR CBaseDialog::DoModal() { HWND unique_hwnd{ m_unique_hwnd[GetDialogName()] }; if (unique_hwnd != NULL && !GetDialogName().IsEmpty()) ///如果对话框已存在,则显示已存在的对话框 { ::ShowWindow(unique_hwnd, SW_RESTORE); ::SetForegroundWindow(unique_hwnd); return 0; } return CDialog::DoModal(); } BOOL CBaseDialog::OnEraseBkgnd(CDC* pDC) { // 修改窗口背景(CDialogEx) if (m_brBkgr.GetSafeHandle() != NULL) { ASSERT_VALID(pDC); CRect rectClient; GetClientRect(rectClient); pDC->FillRect(rectClient, &m_brBkgr); return TRUE; } return CDialog::OnEraseBkgnd(pDC); } HBRUSH CBaseDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { // 修改窗口背景(CDialogEx) if (m_brBkgr.GetSafeHandle() != NULL) { #define AFX_MAX_CLASS_NAME 255 #define AFX_STATIC_CLASS _T("Static") #define AFX_BUTTON_CLASS _T("Button") #define AFX_SLIDER_CLASS _T("msctls_trackbar32") // 滑动条控件CSliderCtrl及其派生类 #define AFX_SYSLINK_CLASS _T("SysLink") // 超链接控件CSysLink及其派生类 if (nCtlColor == CTLCOLOR_STATIC) { TCHAR lpszClassName[AFX_MAX_CLASS_NAME + 1]; ::GetClassName(pWnd->GetSafeHwnd(), lpszClassName, AFX_MAX_CLASS_NAME); CString strClass = lpszClassName; if (strClass == AFX_BUTTON_CLASS || strClass == AFX_STATIC_CLASS || strClass == AFX_SLIDER_CLASS || strClass == AFX_SYSLINK_CLASS) { pDC->SetBkMode(TRANSPARENT); if (m_brBkgr.GetSafeHandle() != NULL && IsAppThemed()) { return (HBRUSH)m_brBkgr.GetSafeHandle(); } else { return (HBRUSH)::GetStockObject(HOLLOW_BRUSH); } } } } return CDialog::OnCtlColor(pDC, pWnd, nCtlColor); } ================================================ FILE: TrafficMonitor/BaseDialog.h ================================================ #pragma once #include // CBaseDialog 对话框 //用于实现记住对话框大小 //并将窗口初始大小设置为最小大小 class CBaseDialog : public CDialog { DECLARE_DYNAMIC(CBaseDialog) public: CBaseDialog(UINT nIDTemplate, CWnd* pParent = NULL); // 标准构造函数 virtual ~CBaseDialog(); // 复制自CDialogEx,与其功能相同(新增滑动条控件和超链接控件的处理) void SetBackgroundColor(COLORREF color, BOOL bRepaint = TRUE); // 对话框数据 //#ifdef AFX_DESIGN_TIME // enum { IDD = IDD_BASEDIALOG }; //#endif void SetMinSize(int cx, int cy); //设置窗口的最小大小,如果未设置,则使用窗口的初始大小作为最小大小 static HWND GetUniqueHandel(LPCTSTR dlg_name); //获指定窗口唯一的句柄 static const std::map& AllUniqueHandels(); //获取所有窗口的句柄 static bool IsAllDialogClosed(); private: void LoadConfig(); void SaveConfig() const; void IterateControls(CWnd* pParent, std::function func); // 重新应用布局管理器参数,这会使控件的基础大小/位置以当前为准 void ReLoadLayoutResource(); private: UINT m_nDialogID; // 成员变量用于保存资源 ID CSize m_min_size{}; //窗口的最小大小(以 96dpi 的大小保存) CSize m_window_size{ -1, -1 }; CBrush m_brBkgr; CDC* m_pDC = nullptr; // InitializeControls期间有效,用于测量文本长度 static std::map m_unique_hwnd; //针对每一个基类的唯一的窗口句柄 bool m_remember_dlg_size{ true }; //是否记住窗口大小(当此标志为true且GetDialogName返回字符串不为空时会记住窗口大小) CFont m_dlg_font; int m_dpi; //窗口自己的DPI protected: int DPI(int pixel) const; // 仅在InitializeControls期间可用,测量控件文本长度 CRect GetTextExtent(const CString& text); struct CtrlTextInfo { // 这里的枚举作为能够类型检查的int使用 // Col具体值大小无意义,只表示列之间的相对顺序以及哪些控件同一列,实际上没有数量限制,需要的话可以增加 enum Col { L4 = -4, L3 = -3, L2 = -2, L1 = -1, C0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, UN_USE = 100 }; // 控件除去文本后剩余的宽度。 // Static静态文本推荐W0,按钮因为可能有图标推荐W32 enum Width { W_50 = -50, W0 = 0, W16 = 16, W24 = 24, W32 = 32, W40 = 40, W60 = 60, W64 = 64, W96 = 96, W128 = 128, W256 = 256 }; Col col_index{ UN_USE }; // 指示控件的位置 ,从左向右递增,小于0左贴靠,大于0右贴靠,等于0使用剩余空间 UINT id{ 0 }; // 控件ID Width ext_width{ W0 }; // 控件宽度至少需要“文字宽度+ext_width”(内部会执行DPI转换 theApp.DPI(ext_width)) }; // 仅在InitializeControls期间可用,根据文本长度重排控件,不会进行任何垂直方向调整,不会改变控件间距 // 只会增加控件宽度故推荐在资源中设置更小的宽度,使用此方法调整到合适的状态 // 优先保证中间的剩余宽度(或col_index为0的控件宽度)至少为center_min_width // 空间不足时其他控件文字会无法完全显示(此时应重新设计窗口)(此方法仅适用于文字不可能太长的情况) void RepositionTextBasedControls(const vector& items, CtrlTextInfo::Width center_min_width = CtrlTextInfo::W16); // 由CBaseDialog::OnInitDialog在还原配置中窗口大小前调用 // 派生类执行部分控件初始化,比如设置控件文本,重排控件RepositionTextBasedControls // 返回true会重新应用布局管理器参数,这会使控件动态布局管理器的基础大小/位置以当前为准 // 与实际窗口大小相关的初始化(比如表格列宽)应在派生类的OnInitDialog进行 virtual bool InitializeControls() { return false; }; virtual CString GetDialogName() const { return CString(); } void EnableDlgCtrl(UINT id, bool enable); void SetButtonIcon(UINT id, HICON hIcon); void SetRememberDlgSize(bool enable) { m_remember_dlg_size = enable; } CRect GetControlRect(CWnd* pCtrl); CRect GetControlRect(UINT id); //遍历所有子控件 void IterateControls(std::function func); virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnDestroy(); afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI); afx_msg void OnSize(UINT nType, int cx, int cy); virtual INT_PTR DoModal(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); }; ================================================ FILE: TrafficMonitor/CAutoAdaptSettingsDlg.cpp ================================================ // CAutoAdaptSettingsDlg.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "CAutoAdaptSettingsDlg.h" #include "afxdialogex.h" // CAutoAdaptSettingsDlg 对话框 IMPLEMENT_DYNAMIC(CAutoAdaptSettingsDlg, CBaseDialog) CAutoAdaptSettingsDlg::CAutoAdaptSettingsDlg(TaskBarSettingData& data, CWnd* pParent /*=nullptr*/) : CBaseDialog(IDD_ATUO_ADAPT_SETTING_DIALOG, pParent), m_data(data) { } CAutoAdaptSettingsDlg::~CAutoAdaptSettingsDlg() { } void CAutoAdaptSettingsDlg::InitComboBox(CComboBox& combo_box, int style_sel) { for (int i = 0; i < TASKBAR_DEFAULT_STYLE_NUM; i++) combo_box.AddString(CCommon::LoadText(IDS_PRESET, std::to_wstring(i + 1).c_str())); if (style_sel >= 0 && style_sel < TASKBAR_DEFAULT_STYLE_NUM) combo_box.SetCurSel(style_sel); else combo_box.SetCurSel(0); } int CAutoAdaptSettingsDlg::GetComboBoxSel(const CComboBox& combo_box) { int sel = combo_box.GetCurSel(); if (sel >= 0 && sel < TASKBAR_DEFAULT_STYLE_NUM) return sel; else return 0; } void CAutoAdaptSettingsDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_DARK_MODE_DEFAULT_STYLE_COMBO, m_dark_mode_default_style_combo); DDX_Control(pDX, IDC_LIGHT_MODE_DEFAULT_STYLE_COMBO, m_light_mode_default_style_combo); } CString CAutoAdaptSettingsDlg::GetDialogName() const { return _T("AutoAdaptSettingsDlg"); } bool CAutoAdaptSettingsDlg::InitializeControls() { RepositionTextBasedControls({ { CtrlTextInfo::L1, IDC_DARK_MODE_STATIC }, { CtrlTextInfo::C0, IDC_DARK_MODE_DEFAULT_STYLE_COMBO }, { CtrlTextInfo::L1, IDC_LIGHT_MODE_STATIC }, { CtrlTextInfo::C0, IDC_LIGHT_MODE_DEFAULT_STYLE_COMBO } }); return true; } BEGIN_MESSAGE_MAP(CAutoAdaptSettingsDlg, CBaseDialog) END_MESSAGE_MAP() // CAutoAdaptSettingsDlg 消息处理程序 BOOL CAutoAdaptSettingsDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 InitComboBox(m_dark_mode_default_style_combo, m_data.dark_default_style); InitComboBox(m_light_mode_default_style_combo, m_data.light_default_style); CheckDlgButton(IDC_AUTO_SAVE_TO_PRESET_CHECK, m_data.auto_save_taskbar_color_settings_to_preset); m_toolTip.Create(this); m_toolTip.SetMaxTipWidth(theApp.DPI(300)); m_toolTip.AddTool(GetDlgItem(IDC_AUTO_SAVE_TO_PRESET_CHECK), CCommon::LoadText(IDS_AUTO_SAVE_TO_PRESET_TIP)); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CAutoAdaptSettingsDlg::OnOK() { // TODO: 在此添加专用代码和/或调用基类 //获取控件中的设置 m_data.dark_default_style = GetComboBoxSel(m_dark_mode_default_style_combo); m_data.light_default_style = GetComboBoxSel(m_light_mode_default_style_combo); m_data.auto_save_taskbar_color_settings_to_preset = (IsDlgButtonChecked(IDC_AUTO_SAVE_TO_PRESET_CHECK) != 0); CBaseDialog::OnOK(); } BOOL CAutoAdaptSettingsDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_MOUSEMOVE) m_toolTip.RelayEvent(pMsg); return CBaseDialog::PreTranslateMessage(pMsg); } ================================================ FILE: TrafficMonitor/CAutoAdaptSettingsDlg.h ================================================ #pragma once #include "BaseDialog.h" // CAutoAdaptSettingsDlg 对话框 class CAutoAdaptSettingsDlg : public CBaseDialog { DECLARE_DYNAMIC(CAutoAdaptSettingsDlg) public: CAutoAdaptSettingsDlg(TaskBarSettingData& data, CWnd* pParent = nullptr); // 标准构造函数 virtual ~CAutoAdaptSettingsDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ATUO_ADAPT_SETTING_DIALOG }; #endif private: CComboBox m_dark_mode_default_style_combo; CComboBox m_light_mode_default_style_combo; TaskBarSettingData& m_data; CToolTipCtrl m_toolTip; private: void InitComboBox(CComboBox& combo_box, int style_sel); int GetComboBoxSel(const CComboBox& combo_box); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 virtual CString GetDialogName() const override; virtual bool InitializeControls() override; DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); virtual void OnOK(); virtual BOOL PreTranslateMessage(MSG* pMsg); }; ================================================ FILE: TrafficMonitor/CMFCColorDialogEx.cpp ================================================ // CMFCColorDialogEx.cpp: 实现文件 // #include "stdafx.h" #include "CMFCColorDialogEx.h" #include "Common.h" #include "TrafficMonitor.h" // CMFCColorDialogEx //IMPLEMENT_DYNAMIC(CMFCColorDialogEx, CMFCColorDialog) CMFCColorDialogEx::CMFCColorDialogEx(COLORREF clrInit, DWORD dwFlags, CWnd* pParentWnd, HPALETTE hPal) :CMFCColorDialog(clrInit, dwFlags, pParentWnd, hPal) { } CMFCColorDialogEx::~CMFCColorDialogEx() { } BEGIN_MESSAGE_MAP(CMFCColorDialogEx, CMFCColorDialog) END_MESSAGE_MAP() // CMFCColorDialogEx 消息处理程序 BOOL CMFCColorDialogEx::OnInitDialog() { CMFCColorDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 //初始化字体 CRect rect; GetWindowRect(rect); FontInfo font_info; font_info.name = theApp.m_str_table.GetLanguageInfo().default_font_name.c_str(); font_info.size = 9; UINT dpi_x{}, dpi_y{}; int dpi = theApp.GetDpi(); if (theApp.DPIFromRect(rect, &dpi_x, &dpi_y)) dpi = dpi_x; font_info.Create(m_dlg_font, dpi); CCommon::SetDialogFont(this, &m_dlg_font); CWnd* pPropSheet = m_pColourSheetOne->GetParent(); if (pPropSheet != nullptr) CCommon::SetDialogFont(pPropSheet, &m_dlg_font); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } ================================================ FILE: TrafficMonitor/CMFCColorDialogEx.h ================================================ #pragma once // CMFCColorDialogEx class CMFCColorDialogEx : public CMFCColorDialog { //DECLARE_DYNAMIC(CMFCColorDialogEx) public: CMFCColorDialogEx(COLORREF clrInit = 0, DWORD dwFlags = 0 /* reserved */, CWnd* pParentWnd = NULL, HPALETTE hPal = NULL); virtual ~CMFCColorDialogEx(); protected: CFont m_dlg_font; protected: DECLARE_MESSAGE_MAP() virtual BOOL OnInitDialog(); }; ================================================ FILE: TrafficMonitor/CSkinPreviewView.cpp ================================================ // CSkinPreviewView.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "CSkinPreviewView.h" // CSkinPreviewView IMPLEMENT_DYNCREATE(CSkinPreviewView, CScrollView) CSkinPreviewView::CSkinPreviewView() { } CSkinPreviewView::~CSkinPreviewView() { } BEGIN_MESSAGE_MAP(CSkinPreviewView, CScrollView) END_MESSAGE_MAP() // CSkinPreviewView 绘图 void CSkinPreviewView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); CSize sizeTotal; // TODO: 计算此视图的合计大小 m_size.cx = 0; m_size.cy = 0; SetScrollSizes(MM_TEXT, m_size); } void CSkinPreviewView::OnDraw(CDC* pDC) { CDrawCommon drawer; drawer.Create(pDC, nullptr); CRect view_rect; GetClientRect(view_rect); view_rect.right = (std::max)(view_rect.Width(), static_cast(m_size.cx)); view_rect.bottom = (std::max)(view_rect.Height(), static_cast(m_size.cy)); //如果皮肤是png格式,绘制10x10的棋盘背景 if (m_skin_data->IsPNG()) { int grid_size = theApp.DPI(10); // 检查画布大小 int rows = view_rect.Height() / grid_size + 1; // 行数 int cols = view_rect.Width() / grid_size + 1; // 列数 // 遍历每个网格 for (int row = 0; row < rows; ++row) { for (int col = 0; col < cols; ++col) { // 计算当前网格的矩形区域 CRect rect(col * grid_size, row * grid_size, (col + 1) * grid_size, (row + 1) * grid_size); // 判断当前网格颜色(交替填充) COLORREF color = ((row + col) % 2 == 0) ? RGB(204, 204, 204) : RGB(254, 254, 254); // 填充矩形 drawer.FillRect(rect, color); } } } //绘制纯色背景 else { drawer.FillRect(view_rect, GetSysColor(COLOR_WINDOW)); } //绘制预览图 CRect draw_rect(CPoint(0, 0), m_size); m_skin_data->DrawPreview(pDC, draw_rect); } // CSkinPreviewView 诊断 //#ifdef _DEBUG //void CSkinPreviewView::AssertValid() const //{ // CScrollView::AssertValid(); //} //#ifndef _WIN32_WCE //void CSkinPreviewView::Dump(CDumpContext& dc) const //{ // CScrollView::Dump(dc); //} //#endif //#endif //_DEBUG void CSkinPreviewView::InitialUpdate() { OnInitialUpdate(); } void CSkinPreviewView::SetSize(int width, int hight) { m_size = CSize(width, hight); SetScrollSizes(MM_TEXT, m_size); } // CSkinPreviewView 消息处理程序 ================================================ FILE: TrafficMonitor/CSkinPreviewView.h ================================================ #pragma once #include "DrawCommon.h" #include "SkinFile.h" // CSkinPreviewView 视图 class CSkinPreviewView : public CScrollView { DECLARE_DYNCREATE(CSkinPreviewView) protected: CSkinPreviewView(); // 动态创建所使用的受保护的构造函数 virtual ~CSkinPreviewView(); public: //#ifdef _DEBUG // virtual void AssertValid() const; //#ifndef _WIN32_WCE // virtual void Dump(CDumpContext& dc) const; //#endif //#endif //成员函数 public: void InitialUpdate(); void SetSize(int width, int hight); void SetSkinData(CSkinFile* skin_data) { m_skin_data = skin_data; } //成员变量 protected: CSize m_size; CPoint m_start_point; //绘图的起始位置 CSkinFile* m_skin_data; protected: virtual void OnDraw(CDC* pDC); // 重写以绘制该视图 virtual void OnInitialUpdate(); // 构造后的第一次 DECLARE_MESSAGE_MAP() }; ================================================ FILE: TrafficMonitor/CTabCtrlEx.cpp ================================================ // CTabCtrlEx.cpp: 实现文件 // #include "stdafx.h" #include "CTabCtrlEx.h" // CTabCtrlEx IMPLEMENT_DYNAMIC(CTabCtrlEx, CTabCtrl) CTabCtrlEx::CTabCtrlEx() { } CTabCtrlEx::~CTabCtrlEx() { } void CTabCtrlEx::AddWindow(CWnd* pWnd, LPCTSTR lable_text) { if (pWnd == nullptr || pWnd->GetSafeHwnd() == NULL) return; InsertItem(m_tab_list.size(), lable_text, m_tab_list.size()); pWnd->SetParent(this); pWnd->MoveWindow(m_tab_rect); m_tab_list.push_back(pWnd); } void CTabCtrlEx::SetCurTab(int index) { if (index < 0 || index >= static_cast(m_tab_list.size())) index = 0; SetCurSel(index); int tab_size = m_tab_list.size(); for (int i = 0; i < tab_size; i++) { if (i == index) { m_tab_list[i]->ShowWindow(SW_SHOW); m_tab_list[i]->SetFocus(); } else { m_tab_list[i]->ShowWindow(SW_HIDE); } } } CWnd* CTabCtrlEx::GetCurrentTab() { int cur_tab_index = GetCurSel(); if (cur_tab_index >= 0 && cur_tab_index < m_tab_list.size()) { return m_tab_list[cur_tab_index]; } return nullptr; } void CTabCtrlEx::AdjustTabWindowSize() { CalSubWindowSize(); for (size_t i{}; i < m_tab_list.size(); i++) { m_tab_list[i]->MoveWindow(m_tab_rect); } } void CTabCtrlEx::CalSubWindowSize() { GetClientRect(m_tab_rect); CRect rc_temp = m_tab_rect; AdjustRect(FALSE, rc_temp); int margin = rc_temp.left - m_tab_rect.left; CRect rcTabItem; GetItemRect(0, rcTabItem); m_tab_rect.top += rcTabItem.Height() + margin; m_tab_rect.left += margin; m_tab_rect.bottom -= margin; m_tab_rect.right -= margin; } BEGIN_MESSAGE_MAP(CTabCtrlEx, CTabCtrl) ON_NOTIFY_REFLECT(TCN_SELCHANGE, &CTabCtrlEx::OnTcnSelchange) ON_WM_SIZE() END_MESSAGE_MAP() // CTabCtrlEx 消息处理程序 void CTabCtrlEx::OnTcnSelchange(NMHDR *pNMHDR, LRESULT *pResult) { // TODO: 在此添加控件通知处理程序代码 int tab_selected = GetCurSel(); SetCurTab(tab_selected); *pResult = 0; } void CTabCtrlEx::PreSubclassWindow() { // TODO: 在此添加专用代码和/或调用基类 //计算子窗口的位置 CalSubWindowSize(); CTabCtrl::PreSubclassWindow(); } void CTabCtrlEx::OnSize(UINT nType, int cx, int cy) { CTabCtrl::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 AdjustTabWindowSize(); } ================================================ FILE: TrafficMonitor/CTabCtrlEx.h ================================================ #pragma once // CTabCtrlEx class CTabCtrlEx : public CTabCtrl { DECLARE_DYNAMIC(CTabCtrlEx) public: CTabCtrlEx(); virtual ~CTabCtrlEx(); void AddWindow(CWnd* pWnd, LPCTSTR lable_text); //ǰtabؼһӴ void SetCurTab(int index); CWnd* GetCurrentTab(); void AdjustTabWindowSize(); protected: void CalSubWindowSize(); DECLARE_MESSAGE_MAP() protected: vector m_tab_list; //tabؼÿӴڵָ public: afx_msg void OnTcnSelchange(NMHDR *pNMHDR, LRESULT *pResult); virtual void PreSubclassWindow(); CRect m_tab_rect; afx_msg void OnSize(UINT nType, int cx, int cy); }; ================================================ FILE: TrafficMonitor/CVariant.cpp ================================================ #include "stdafx.h" #include "CVariant.h" CVariant::CVariant(int value) { m_value_int = value; m_type = eType::INT; } CVariant::CVariant(size_t value) { m_value_int = static_cast(value); m_type = eType::UINT; } CVariant::CVariant(double value) { m_value_double = value; m_type = eType::DOUBLE; } CVariant::CVariant(LPCTSTR value) { m_value_string = value; m_type = eType::STRING; } CVariant::CVariant(const CString& value) { m_value_string = value; m_type = eType::STRING; } CVariant::CVariant(const wstring & value) { m_value_string = value.c_str(); m_type = eType::STRING; } CVariant::~CVariant() { } CString CVariant::ToString() const { CString str; switch (m_type) { case CVariant::eType::INT: str.Format(_T("%d"), m_value_int); break; case eType::UINT: str.Format(_T("%u"), static_cast(m_value_int)); break; case CVariant::eType::DOUBLE: str.Format(_T("%g"), m_value_double); break; case CVariant::eType::STRING: str = m_value_string; break; default: break; } return str; } ================================================ FILE: TrafficMonitor/CVariant.h ================================================ #pragma once class CVariant { public: CVariant(int value); CVariant(size_t value); CVariant(double value); CVariant(LPCTSTR value); CVariant(const CString& value); CVariant(const wstring& value); ~CVariant(); CString ToString() const; private: enum class eType { INT, UINT, DOUBLE, STRING }; int m_value_int; double m_value_double; CString m_value_string; eType m_type; }; ================================================ FILE: TrafficMonitor/CalendarHelper.cpp ================================================ #include "stdafx.h" #include "CalendarHelper.h" CCalendarHelper::CCalendarHelper() { } CCalendarHelper::~CCalendarHelper() { } bool CCalendarHelper::IsLeapYear(int year) { return ((0 == year % 4 && 0 != year % 100) || 0 == year % 400); } int CCalendarHelper::CaculateWeekDay(int y, int m, int d) { if (m <= 2) { m += 12; y--; } return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7; } int CCalendarHelper::DaysInMonth(int year, int month) { bool leap{ IsLeapYear(year) }; switch (month) { case 2: if (leap) return 29; else return 28; case 4: case 6: case 9: case 11: return 30; default: return 31; } } void CCalendarHelper::GetCalendar(int year, int month, DayTraffic calendar[CALENDAR_HEIGHT][CALENDAR_WIDTH], bool sunday_first) { memset(calendar, 0, sizeof(DayTraffic)*CALENDAR_HEIGHT*CALENDAR_WIDTH); int days{ DaysInMonth(year, month) }; int first_weak_day{ CaculateWeekDay(year, month, 1) }; if(!sunday_first) { first_weak_day--; if (first_weak_day < 0) first_weak_day = 6; } int i{}, j{}; for (int n{}; n < 37; n++) { if (n < first_weak_day) { calendar[i][j].day = 0; } else { int day = n - first_weak_day + 1; if (day <= days) calendar[i][j].day = day; } j++; if (j >= 7) { j = 0; i++; } } } ================================================ FILE: TrafficMonitor/CalendarHelper.h ================================================ #pragma once #define CALENDAR_WIDTH 7 #define CALENDAR_HEIGHT 6 struct DayTraffic { int day; __int64 up_traffic; __int64 down_traffic; bool mixed; CRect rect; __int64 traffic() const { return up_traffic + down_traffic; } }; class CCalendarHelper { public: CCalendarHelper(); ~CCalendarHelper(); //ǷΪ static bool IsLeapYear(int year); //ڼ(0~6~) static int CaculateWeekDay(int y, int m, int d); //һж static int DaysInMonth(int year, int month); //ȡָ·ݵݣcalendar //sunday_firstΪtrueΪÿܵĵһ죬򣬽һΪÿܵĵһ static void GetCalendar(int year, int month, DayTraffic calendar[CALENDAR_HEIGHT][CALENDAR_WIDTH], bool sunday_first = true); }; ================================================ FILE: TrafficMonitor/ClassicalTaskbarDlg.cpp ================================================ #include "stdafx.h" #include "ClassicalTaskbarDlg.h" void CClassicalTaskbarDlg::AdjustTaskbarWndPos(bool force_adjust) { ::GetWindowRect(m_hMin, m_rcMin); //获得最小化窗口的区域 ::GetWindowRect(m_hBar, m_rcBar); //获得二级容器的区域 if (m_taskbar_on_top_or_bottom) //当任务栏在桌面顶部或底部时 { //设置窗口大小 m_rect.right = m_rect.left + m_window_width; m_rect.bottom = m_rect.top + m_window_height; if (force_adjust || m_rcMin.Width() != m_last_width) //如果宽度改变了,重新设置任务栏窗口的位置 { m_rcMinOri = m_rcMin; m_left_space = m_rcMin.left - m_rcBar.left; //保存上次的宽度 m_last_width = m_rcMin.Width() - m_rect.Width(); //任务窗口显示在右侧时 if (!theApp.m_taskbar_data.tbar_wnd_on_left) { ::MoveWindow(m_hMin, m_left_space, 0, m_rcMin.Width() - m_rect.Width(), m_rcMin.Height(), TRUE); //设置最小化窗口的位置 m_rect.MoveToX(m_left_space + m_rcMin.Width() - m_rect.Width() + 2); } //任务栏窗口显示在左侧时 else { ::MoveWindow(m_hMin, m_left_space + m_rect.Width(), 0, m_rcMin.Width() - m_rect.Width(), m_rcMin.Height(), TRUE); m_rect.MoveToX(m_left_space); } //设置任务栏窗口的垂直位置 m_rect.MoveToY((m_rcBar.Height() - m_rect.Height()) / 2); if (theApp.m_taskbar_data.horizontal_arrange && theApp.m_win_version.IsWindows7()) m_rect.MoveToY(m_rect.top + DPI(1)); MoveWindow(m_rect); } } else //当任务栏在屏幕在左侧或右侧时 { //设置窗口大小 if (force_adjust || m_rcMin.Height() != m_last_height) //如果高度改变了,重新设置任务栏窗口的位置 { m_rcMinOri = m_rcMin; m_top_space = m_rcMin.top - m_rcBar.top; //保存上次的宽度 m_last_height = m_rcMin.Height() - m_rect.Height(); if (!theApp.m_taskbar_data.tbar_wnd_on_left) { ::MoveWindow(m_hMin, 0, m_top_space, m_rcMin.Width(), m_rcMin.Height() - m_rect.Height(), TRUE); //设置最小化窗口的位置 m_rect.MoveToY(m_top_space + m_rcMin.Height() - m_rect.Height() + 2); } else { ::MoveWindow(m_hMin, 0, m_top_space + m_rect.Height(), m_rcMin.Width(), m_rcMin.Height() - m_rect.Height(), TRUE); //设置最小化窗口的位置 m_rect.MoveToY(m_top_space); } m_rect.MoveToX((m_rcMin.Width() - m_window_width) / 2); int left_space = DPI(2); if (m_rect.left < left_space) m_rect.MoveToX(left_space); MoveWindow(m_rect); } } } void CClassicalTaskbarDlg::InitTaskbarWnd() { m_hBar = ::FindWindowEx(m_hTaskbar, 0, L"ReBarWindow32", NULL); //寻找二级容器的句柄 if (m_hBar == NULL) m_hBar = ::FindWindowEx(m_hTaskbar, nullptr, L"WorkerW", NULL); m_hMin = ::FindWindowEx(m_hBar, 0, L"MSTaskSwWClass", NULL); //寻找最小化窗口的句柄 if (m_hMin == NULL) m_hMin = ::FindWindowEx(m_hBar, 0, L"MSTaskListWClass", NULL); //寻找最小化窗口的句柄 ::GetWindowRect(m_hMin, m_rcMin); //获得最小化窗口的区域 ::GetWindowRect(m_hBar, m_rcBar); //获得二级容器的区域 m_left_space = m_rcMin.left - m_rcBar.left; m_top_space = m_rcMin.top - m_rcBar.top; } void CClassicalTaskbarDlg::ResetTaskbarPos() { //程序关闭的时候,把最小化窗口的width恢复回去 if (!m_rcMinOri.IsRectEmpty()) { if (m_taskbar_on_top_or_bottom) ::MoveWindow(m_hMin, m_left_space, 0, m_rcMinOri.Width(), m_rcMinOri.Height(), TRUE); else ::MoveWindow(m_hMin, 0, m_top_space, m_rcMinOri.Width(), m_rcMinOri.Height(), TRUE); } } HWND CClassicalTaskbarDlg::GetParentHwnd() { return m_hBar; } void CClassicalTaskbarDlg::CheckTaskbarOnTopOrBottom() { CRect rect; CRect rcMin; CRect rcBar; if (m_hTaskbar != 0) { //::GetWindowRect(m_hMin, rcMin); //获得最小化窗口的区域 //::GetWindowRect(m_hBar, rcBar); //获得二级容器的区域 //if (m_left_space == 0) // m_left_space = rcMin.left - rcBar.left; //if (m_top_space == 0) // m_top_space = rcMin.top - rcBar.top; ::GetWindowRect(m_hTaskbar, rect); //获取任务栏的矩形区域 m_taskbar_on_top_or_bottom = (rect.Width() >= rect.Height()); //如果任务栏的宽度大于高度,则任务在屏幕的顶部或底部 } else { m_taskbar_on_top_or_bottom = true; } } ================================================ FILE: TrafficMonitor/ClassicalTaskbarDlg.h ================================================ #pragma once #include "TaskBarDlg.h" class CClassicalTaskbarDlg : public CTaskBarDlg { public: private: // 通过 CTaskBarDlg 继承 virtual void AdjustTaskbarWndPos(bool force_adjust) override; void InitTaskbarWnd() override; void ResetTaskbarPos() override; virtual HWND GetParentHwnd() override; private: CRect m_rcMinOri; //初始状态时最小化窗口的矩形区域 int m_left_space{}; //最小化窗口和二级窗口窗口左侧的边距 int m_top_space{}; //最小化窗口和二级窗口窗口顶部的边距(用于任务栏在屏幕左侧或右侧时) HWND m_hBar; //任务栏窗口二级容器的句柄 HWND m_hMin; //最小化窗口的句柄 CRect m_rcBar; //初始状态时任务栏窗口的矩形区域 CRect m_rcMin; //最小化窗口的矩形区域 int m_last_width; //用于检测宽度变化的上一次的宽度 int m_last_height; //用于检测高度变化的上一次的高度(用于任务栏在屏幕左侧或右侧时) // 通过 CTaskBarDlg 继承 void CheckTaskbarOnTopOrBottom() override; }; ================================================ FILE: TrafficMonitor/ColorSettingListCtrl.cpp ================================================ #include "stdafx.h" #include "ColorSettingListCtrl.h" IMPLEMENT_DYNAMIC(CColorSettingListCtrl, CListCtrl) CColorSettingListCtrl::CColorSettingListCtrl() { } CColorSettingListCtrl::~CColorSettingListCtrl() { } void CColorSettingListCtrl::SetItemColor(int row, int col, COLORREF color) { m_colors[row][col] = color; } COLORREF CColorSettingListCtrl::GetItemColor(int row, int col) { return m_colors[row][col]; } BEGIN_MESSAGE_MAP(CColorSettingListCtrl, CListCtrl) ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CColorSettingListCtrl::OnNMCustomdraw) END_MESSAGE_MAP() void CColorSettingListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) { *pResult = CDRF_DODEFAULT; LPNMLVCUSTOMDRAW lplvdr = reinterpret_cast(pNMHDR); NMCUSTOMDRAW& nmcd = lplvdr->nmcd; switch (lplvdr->nmcd.dwDrawStage) //判断状态 { case CDDS_PREPAINT: *pResult = CDRF_NOTIFYITEMDRAW; break; case CDDS_ITEMPREPAINT: //如果为画ITEM之前就要进行颜色的改变 if (nmcd.dwItemSpec >= 0 && nmcd.dwItemSpec < GetItemCount()) { //double range = m_item_rage_data[nmcd.dwItemSpec].data_value; //CDC* pDC = CDC::FromHandle(nmcd.hdc); //获取绘图DC //CRect item_rect, draw_rect; //GetSubItemRect(nmcd.dwItemSpec,m_draw_item_range_row, LVIR_BOUNDS, item_rect); //获取绘图单元格的矩形区域 //CDrawCommon::SetDrawRect(pDC, item_rect); //设置绘图区域为当前列 //draw_rect = item_rect; //if (draw_rect.Height() > 2 * m_margin) //{ // draw_rect.top += m_margin; // draw_rect.bottom -= m_margin; //} //int width; //if (m_use_log_scale) //使用对数比例(y=ln(x+1)) //{ // range = std::log(range + 1); // width = static_cast(range*draw_rect.Width() / std::log(1000 + 1)); //} //else //使用线性比例(y=x) //{ // width = static_cast(range*draw_rect.Width() / 1000); //} //draw_rect.right = draw_rect.left + width; //pDC->FillSolidRect(draw_rect, m_item_rage_data[nmcd.dwItemSpec].color); ////当前列绘制完成后将绘图区域设置为左边的区域,防止当前列的区域被覆盖 //CRect rect1{ item_rect }; //rect1.left = 0; //rect1.right = item_rect.left; //CDrawCommon::SetDrawRect(pDC, rect1); CDC* pDC = CDC::FromHandle(nmcd.hdc); //获取绘图DC CRect item_rect; auto& col_color_map = m_colors[nmcd.dwItemSpec]; for (auto iter = col_color_map.begin(); iter != col_color_map.end(); ++iter) { GetSubItemRect(nmcd.dwItemSpec, iter->first, LVIR_BOUNDS, item_rect); //获取绘图单元格的矩形区域 //设置绘图剪辑区域 CDrawCommon::SetDrawRect(pDC, item_rect); //使用双缓冲绘图 CDrawDoubleBuffer draw_double_buffer(pDC, item_rect); //填充背景 item_rect.MoveToXY(0, 0); draw_double_buffer.GetMemDC()->FillSolidRect(item_rect, GetSysColor(COLOR_WINDOW)); //绘制颜色矩形 item_rect.DeflateRect(m_margin, m_margin); draw_double_buffer.GetMemDC()->FillSolidRect(item_rect, iter->second); //绘制矩形边框 CDrawCommon drawer; drawer.Create(draw_double_buffer.GetMemDC(), this); drawer.DrawRectOutLine(item_rect, RGB(192, 192, 192)); } //当前列绘制完成后将绘图区域设置为第一列的区域,防止颜色列的区域被覆盖 CRect rect1{}; GetSubItemRect(nmcd.dwItemSpec, 1, LVIR_BOUNDS, rect1); //获取第1列单元格的矩形区域 rect1.right = rect1.left; rect1.left = 0; CDrawCommon::SetDrawRect(pDC, rect1); } *pResult = CDRF_DODEFAULT; break; } } ================================================ FILE: TrafficMonitor/ColorSettingListCtrl.h ================================================ #pragma once #include "afxcmn.h" #include "DrawCommon.h" class CColorSettingListCtrl : public CListCtrl { DECLARE_DYNAMIC(CColorSettingListCtrl) public: CColorSettingListCtrl(); ~CColorSettingListCtrl(); void SetItemColor(int row, int col, COLORREF color); COLORREF GetItemColor(int row, int col); void SetDrawItemRangMargin(int margin) { m_margin = margin; } //ûƻƵľεıԵԪ߿ľΣֵԽƵľԽϸDzܳбоһ protected: int m_margin{}; std::map> m_colors; //ڱÿһÿһеɫ DECLARE_MESSAGE_MAP() afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult); }; ================================================ FILE: TrafficMonitor/ColorStatic.cpp ================================================ // ColorStatic.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "ColorStatic.h" #include "DrawCommon.h" // CColorStatic IMPLEMENT_DYNAMIC(CColorStatic, CStatic) CColorStatic::CColorStatic() { } CColorStatic::~CColorStatic() { } void CColorStatic::SetFillColor(COLORREF fill_color) { m_colors.resize(1); m_colors[0] = fill_color; //m_fill_color = fill_color; Invalidate(); } void CColorStatic::SetColorNum(int color_num) { if (color_num <= 0 || color_num > 32) color_num = 1; m_colors.resize(color_num); } void CColorStatic::SetFillColor(int index, COLORREF fill_color) { if (index >= 0 && index < static_cast(m_colors.size())) m_colors[index] = fill_color; } void CColorStatic::SetLinkCursor(bool link_cursor) { m_link_cursor = link_cursor; } void CColorStatic::EnableWindow(bool enable) { CStatic::EnableWindow(enable); Invalidate(FALSE); } BEGIN_MESSAGE_MAP(CColorStatic, CStatic) ON_WM_PAINT() ON_WM_MOUSEHOVER() ON_WM_MOUSELEAVE() ON_WM_SETCURSOR() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() END_MESSAGE_MAP() // CColorStatic 消息处理程序 void CColorStatic::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CStatic::OnPaint() CRect rect; GetClientRect(rect); rect.MoveToXY(0, 0); CRect rc_tmp{ rect }; CDrawCommon draw; draw.Create(&dc, this); int color_num = static_cast(m_colors.size()); if (IsWindowEnabled() && color_num > 0) { if (color_num == 1) { dc.FillSolidRect(rect, m_colors[0]); } //颜色数量大于或等于4,并且是4个位数时,上下两行以“Z”字形排列 else if (color_num >= 4 && color_num % 4 == 0) { dc.FillSolidRect(rect, RGB(255, 255, 255)); int group_num = color_num / 4; //颜色组数(4个为一组) for (int i = 0; i < group_num; i++) { int group_left = rect.Width() * i / group_num; int group_right = rect.Width() * (i + 1) / group_num; CRect rc_group(group_left, 0, group_right, rect.Height()); //当前组的矩形区域 CRect rc1(group_left, 0, group_left + rc_group.Width() / 2, rc_group.Height() / 2); CRect rc2(group_left + rc_group.Width() / 2, 0, rc_group.right, rc_group.Height() / 2); CRect rc3(group_left, rc_group.Height() / 2, group_left + rc_group.Width() / 2, rc_group.bottom); CRect rc4(group_left + rc_group.Width() / 2, rc_group.Height() / 2, rc_group.right, rc_group.bottom); dc.FillSolidRect(rc1, m_colors[static_cast(i) * 4]); dc.FillSolidRect(rc2, m_colors[static_cast(i) * 4 + 1]); dc.FillSolidRect(rc3, m_colors[static_cast(i) * 4 + 2]); dc.FillSolidRect(rc4, m_colors[static_cast(i) * 4 + 3]); } } //其他情况,一行从左到右排列 else { dc.FillSolidRect(rect, RGB(255, 255, 255)); for (int i = 0; i < color_num; i++) { int left = rect.Width() * i / color_num; int right = rect.Width() * (i + 1) / color_num; CRect rc_cell(left, 0, right, rect.Height()); dc.FillSolidRect(rc_cell, m_colors[i]); } } //画边框 draw.DrawRectOutLine(rect, RGB(160, 160, 160)); } else { CBrush brush(HS_BDIAGONAL, RGB(160, 160, 160)); dc.FillRect(rect, &brush); draw.DrawRectOutLine(rect, RGB(192, 192, 192)); } } void CColorStatic::OnMouseHover(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 m_hover = true; CStatic::OnMouseHover(nFlags, point); } void CColorStatic::OnMouseLeave() { // TODO: 在此添加消息处理程序代码和/或调用默认值 m_hover = false; CStatic::OnMouseLeave(); } BOOL CColorStatic::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(m_link_cursor && m_hover) { ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(32649))); return TRUE; } return CStatic::OnSetCursor(pWnd, nHitTest, message); } void CColorStatic::PreSubclassWindow() { // TODO: 在此添加专用代码和/或调用基类 DWORD dwStyle = GetStyle(); ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY); ModifyStyleEx(WS_EX_STATICEDGE, NULL); CStatic::PreSubclassWindow(); } void CColorStatic::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (m_link_cursor) { TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_LEAVE | TME_HOVER; tme.dwHoverTime = 1; _TrackMouseEvent(&tme); } CStatic::OnMouseMove(nFlags, point); } void CColorStatic::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //如果单击了鼠标左键,则向父窗口发送一个WM_STATIC_CLICKED消息 CWnd* pParent{ GetParent() }; if (pParent != nullptr) pParent->SendMessage(WM_STATIC_CLICKED, (WPARAM)this); CStatic::OnLButtonUp(nFlags, point); } ================================================ FILE: TrafficMonitor/ColorStatic.h ================================================ // CColorStatic //派生自CStatic,用于显示颜色的Static控件 #pragma once #define WM_STATIC_CLICKED (WM_USER + 1001) class CColorStatic : public CStatic { DECLARE_DYNAMIC(CColorStatic) public: CColorStatic(); virtual ~CColorStatic(); void SetFillColor(COLORREF fill_color); //设置要填充单一的背景色 void SetColorNum(int color_num); //设置颜色的数量 void SetFillColor(int index, COLORREF fill_color); void SetLinkCursor(bool link_cursor = true); //设置指向控件时光标变成超链接形状 void EnableWindow(bool enable); protected: //COLORREF m_fill_color{ RGB(255, 255,255) }; vector m_colors; bool m_hover{ false }; //当鼠标指向控件时为true bool m_link_cursor{ false }; //是否启用超链接形状的光标 protected: DECLARE_MESSAGE_MAP() public: afx_msg void OnPaint(); afx_msg void OnMouseHover(UINT nFlags, CPoint point); afx_msg void OnMouseLeave(); afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); virtual void PreSubclassWindow(); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); }; ================================================ FILE: TrafficMonitor/ComboBox2.cpp ================================================ // ComboBox2.cpp: 实现文件 // #include "stdafx.h" #include "ComboBox2.h" // CComboBox2 IMPLEMENT_DYNAMIC(CComboBox2, CComboBox) CComboBox2::CComboBox2() { } CComboBox2::~CComboBox2() { } void CComboBox2::SetMouseWheelEnable(bool enable) { m_mouse_wheel_enable = enable; } BEGIN_MESSAGE_MAP(CComboBox2, CComboBox) END_MESSAGE_MAP() // CComboBox2 消息处理程序 BOOL CComboBox2::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 //如果m_mouse_wheel_enable为false,则不响应鼠标滚轮消息 if (pMsg->message == WM_MOUSEWHEEL && !m_mouse_wheel_enable) { //将鼠标滚轮消息发送给父窗口 CWnd* pParent = GetParent(); pParent->SendMessage(WM_MOUSEWHEEL, pMsg->wParam, pMsg->lParam); return true; } return CComboBox::PreTranslateMessage(pMsg); } ================================================ FILE: TrafficMonitor/ComboBox2.h ================================================ #pragma once // CComboBox2 class CComboBox2 : public CComboBox { DECLARE_DYNAMIC(CComboBox2) public: CComboBox2(); virtual ~CComboBox2(); void SetMouseWheelEnable(bool enable); //设置是否允许响应鼠标滚轮 private: bool m_mouse_wheel_enable{ true }; protected: DECLARE_MESSAGE_MAP() public: virtual BOOL PreTranslateMessage(MSG* pMsg); }; ================================================ FILE: TrafficMonitor/Common.cpp ================================================ #include "stdafx.h" #include "Common.h" #include "TrafficMonitor.h" CCommon::CCommon() { } CCommon::~CCommon() { } wstring CCommon::StrToUnicode(const char* str, bool utf8) { wstring result; int size; size = MultiByteToWideChar((utf8 ? CP_UTF8 : CP_ACP), 0, str, -1, NULL, 0); if (size <= 0) return wstring(); wchar_t* str_unicode = new wchar_t[size + 1]; MultiByteToWideChar((utf8 ? CP_UTF8 : CP_ACP), 0, str, -1, str_unicode, size); result.assign(str_unicode); delete[] str_unicode; return result; } string CCommon::UnicodeToStr(const wchar_t* wstr, bool utf8) { string result; int size{ 0 }; size = WideCharToMultiByte((utf8 ? CP_UTF8 : CP_ACP), 0, wstr, -1, NULL, 0, NULL, NULL); if (size <= 0) return string(); char* str = new char[size + 1]; WideCharToMultiByte((utf8 ? CP_UTF8 : CP_ACP), 0, wstr, -1, str, size, NULL, NULL); result.assign(str); delete[] str; return result; } wstring CCommon::AsciiToUnicode(const string& str) { std::wstring result; result.resize(str.size()); for (size_t i{}; i < str.size(); i++) result[i] = str[i]; return result; } string CCommon::AsciiToStr(const std::wstring& wstr) { std::string result; result.resize(wstr.size()); for (size_t i{}; i < wstr.size(); i++) result[i] = static_cast(wstr[i]); return result; } template static void _StringNormalize(T& str) { if (str.empty()) return; int size = static_cast(str.size()); //字符串的长度 if (size < 0) return; int index1 = 0; //字符串中第1个不是空格或控制字符的位置 int index2 = size - 1; //字符串中最后一个不是空格或控制字符的位置 while (index1 < size && str[index1] >= 0 && str[index1] <= 32) index1++; while (index2 >= 0 && str[index2] >= 0 && str[index2] <= 32) index2--; if (index1 > index2) //如果index1 > index2,说明字符串全是空格或控制字符 str.clear(); else if (index1 == 0 && index2 == size - 1) //如果index1和index2的值分别为0和size - 1,说明字符串前后没有空格或控制字符,直接返回 return; else str = str.substr(index1, index2 - index1 + 1); } void CCommon::StringNormalize(std::string& str) { _StringNormalize(str); } void CCommon::StringNormalize(std::wstring& str) { _StringNormalize(str); } template static void _StringSplit(const T& str, wchar_t div_ch, vector& results, bool skip_empty = true, bool trim = true) { results.clear(); size_t split_index = -1; size_t last_split_index = -1; while (true) { split_index = str.find(div_ch, split_index + 1); T split_str = str.substr(last_split_index + 1, split_index - last_split_index - 1); if (trim) _StringNormalize(split_str); if (!split_str.empty() || !skip_empty) results.push_back(split_str); if (split_index == wstring::npos) break; last_split_index = split_index; } } void CCommon::StringSplit(const std::string& str, char div_ch, vector& results, bool skip_empty, bool trim) { _StringSplit(str, div_ch, results, skip_empty, trim); } void CCommon::StringSplit(const std::wstring& str, wchar_t div_ch, vector& results, bool skip_empty, bool trim) { _StringSplit(str, div_ch, results, skip_empty, trim); } template static void _StringSplit(const T& str, const T& div_str, vector& results, bool skip_empty = true, bool trim = true) { results.clear(); size_t split_index = 0 - div_str.size(); size_t last_split_index = 0 - div_str.size(); while (true) { split_index = str.find(div_str, split_index + div_str.size()); T split_str = str.substr(last_split_index + div_str.size(), split_index - last_split_index - div_str.size()); if (trim) _StringNormalize(split_str); if (!split_str.empty() || !skip_empty) results.push_back(split_str); if (split_index == wstring::npos) break; last_split_index = split_index; } } void CCommon::StringSplit(const std::string& str, const std::string& div_str, vector& results, bool skip_empty, bool trim) { _StringSplit(str, div_str, results, skip_empty, trim); } void CCommon::StringSplit(const std::wstring& str, const std::wstring& div_str, vector& results, bool skip_empty, bool trim) { _StringSplit(str, div_str, results, skip_empty, trim); } template static bool _StringTransform(T& str, bool upper) { if (str.empty()) return false; if (upper) { for (auto& ch : str) { { if (ch >= 'a' && ch <= 'z') ch -= 32; } } } else { for (auto& ch : str) { if (ch >= 'A' && ch <= 'Z') ch += 32; } } return true; } bool CCommon::StringTransform(std::string& str, bool upper) { return _StringTransform(str, upper); } bool CCommon::StringTransform(std::wstring& str, bool upper) { return _StringTransform(str, upper); } bool CCommon::GetFileContent(const wchar_t* file_path, string& contents_buff, bool binary /*= true*/) { std::ifstream file{ file_path, (binary ? std::ios::binary : std::ios::in) }; if (file.fail()) return false; //获取文件长度 file.seekg(0, file.end); size_t length = file.tellg(); file.seekg(0, file.beg); char* buff = new char[length]; file.read(buff, length); file.close(); contents_buff.assign(buff, length); delete[] buff; return true; } const char* CCommon::GetFileContent(const wchar_t* file_path, size_t& length, bool binary /*= true*/) { std::ifstream file{ file_path, (binary ? std::ios::binary : std::ios::in) }; length = 0; if (file.fail()) return nullptr; //获取文件长度 file.seekg(0, file.end); length = file.tellg(); file.seekg(0, file.beg); char* buff = new char[length]; file.read(buff, length); file.close(); return buff; } CString CCommon::DataSizeToString(unsigned long long size, const PublicSettingData& cfg) { //CString str; CString value_str, unit_str; if (!cfg.unit_byte) //如果使用比特(bit)为单位,则数值乘以8 { size *= 8; } switch (cfg.speed_unit) { case SpeedUnit::AUTO: if (cfg.speed_short_mode) { if (size < 1024 * 10) //10KB以下以KB为单位,保留1位小数 { value_str.Format(_T("%.1f"), size / 1024.0f); unit_str = _T("K"); } else if (size < 1024 * 1000) //1000KB以下以KB为单位,保留整数 { value_str.Format(_T("%.0f"), size / 1024.0f); unit_str = _T("K"); } else if (size < 1024 * 1024 * 1000) //1000MB以下以MB为单位,保留1位小数 { value_str.Format(_T("%.1f"), size / 1024.0f / 1024.0f); unit_str = _T("M"); } else { value_str.Format(_T("%.2f"), size / 1024.0f / 1024.0f / 1024.0f); unit_str = _T("G"); } } else { if (size < 1024 * 10) //10KB以下以KB为单位,保留2位小数 { value_str.Format(_T("%.2f"), size / 1024.0f); unit_str = _T("KB"); } else if (size < 1024 * 1000) //1000KB以下以KB为单位,保留1位小数 { value_str.Format(_T("%.1f"), size / 1024.0f); unit_str = _T("KB"); } else if (size < 1024 * 1024 * 1000) //1000MB以下以MB为单位,保留2位小数 { value_str.Format(_T("%.2f"), size / 1024.0f / 1024.0f); unit_str = _T("MB"); } else { value_str.Format(_T("%.2f"), size / 1024.0f / 1024.0f / 1024.0f); unit_str = _T("GB"); } } break; case SpeedUnit::KBPS: if (cfg.speed_short_mode) { if (size < 1024 * 10) //10KB以下保留1位小数 value_str.Format(_T("%.1f"), size / 1024.0f); else //10KB以上保留整数 value_str.Format(_T("%.0f"), size / 1024.0f); if (!cfg.hide_unit) unit_str = _T("K"); } else { if (size < 1024 * 10) //10KB以下保留2位小数 value_str.Format(_T("%.2f"), size / 1024.0f); else //10KB以上保留1位小数 value_str.Format(_T("%.1f"), size / 1024.0f); if (!cfg.hide_unit) unit_str = _T("KB"); } break; case SpeedUnit::MBPS: if (cfg.speed_short_mode) { value_str.Format(_T("%.1f"), size / 1024.0f / 1024.0f); if (!cfg.hide_unit) unit_str = _T("M"); } else { value_str.Format(_T("%.2f"), size / 1024.0f / 1024.0f); if (!cfg.hide_unit) unit_str = _T("MB"); } break; } CString str; if (cfg.separate_value_unit_with_space && !cfg.hide_unit) str = value_str + _T(' ') + unit_str; else str = value_str + unit_str; if (!cfg.unit_byte) { if (cfg.speed_short_mode && !cfg.hide_unit) str += _T('b'); //如果使用比特(bit)为单位,即使设置了网速简洁模式,也将“b”显示出来 else str.Replace(_T('B'), _T('b')); //如果使用比特(bit)为单位,将B替换成b } return str; } CString CCommon::DataSizeToString(unsigned long long size, bool with_space) { CString str; if (size < 1024 * 10) //10KB以下以KB为单位,保留2位小数 str.Format(_T("%.2f KB"), size / 1024.0); else if (size < 1024 * 1024) //1MB以下以KB为单位,保留1位小数 str.Format(_T("%.1f KB"), size / 1024.0); else if (size < 1024 * 1024 * 1024) //1GB以下以MB为单位,保留2位小数 str.Format(_T("%.2f MB"), size / 1024.0 / 1024.0); else if (size < 1024ll * 1024 * 1024 * 1024) str.Format(_T("%.2f GB"), size / 1024.0 / 1024.0 / 1024.0); else str.Format(_T("%.2f TB"), size / 1024.0 / 1024.0 / 1024.0 / 1024.0); if (!with_space) str.Remove(_T(' ')); return str; } CString CCommon::TemperatureToString(float temperature, const PublicSettingData& cfg) { CString str_val; if (temperature <= 0) str_val = _T("--"); else str_val.Format(_T("%d"), static_cast(temperature)); if (cfg.separate_value_unit_with_space) str_val += _T(' '); str_val += _T("°C"); return str_val; } CString CCommon::UsageToString(int usage, const PublicSettingData& cfg) { CString str_val; if (usage < 0) str_val = _T("--"); else str_val.Format(_T("%d"), usage); if (!cfg.hide_percent) { if (cfg.separate_value_unit_with_space) str_val += _T(' '); str_val += _T('%'); } return str_val; } CString CCommon::FreqToString(float freq, const PublicSettingData& cfg) { CString str_val; if (freq < 0) str_val = _T("--"); else str_val.Format(_T("%.2f"), freq); if (cfg.separate_value_unit_with_space) str_val += _T(' '); str_val += _T("GHz"); return str_val; } //CString CCommon::KBytesToString(unsigned int kb_size) //{ // CString k_bytes_str; // if (kb_size < 1024) // k_bytes_str.Format(_T("%d KB"), kb_size); // else if (kb_size < 1024 * 1024) // k_bytes_str.Format(_T("%.2f MB"), kb_size / 1024.0); // else if (kb_size < 1024 * 1024 * 1024) // k_bytes_str.Format(_T("%.2f GB"), kb_size / 1024.0 / 1024.0); // else // k_bytes_str.Format(_T("%.2f TB"), kb_size / 1024.0 / 1024.0 / 1024.0); // return k_bytes_str; //} CString CCommon::KBytesToString(unsigned __int64 kb_size) { CString k_bytes_str; if (kb_size < 1024) k_bytes_str.Format(_T("%d KB"), kb_size); else if (kb_size < 1024 * 1024) k_bytes_str.Format(_T("%.2f MB"), kb_size / 1024.0); else if (kb_size < 1024 * 1024 * 1024) k_bytes_str.Format(_T("%.2f GB"), kb_size / 1024.0 / 1024.0); else k_bytes_str.Format(_T("%.2f TB"), kb_size / 1024.0 / 1024.0 / 1024.0); return k_bytes_str; } __int64 CCommon::CompareFileTime2(FILETIME time1, FILETIME time2) { __int64 a = static_cast<__int64>(time1.dwHighDateTime) << 32 | time1.dwLowDateTime; __int64 b = static_cast<__int64>(time2.dwHighDateTime) << 32 | time2.dwLowDateTime; return b - a; } void CCommon::WriteLog(const char* str_text, LPCTSTR file_path) { SYSTEMTIME cur_time; GetLocalTime(&cur_time); char buff[32]; sprintf_s(buff, "%d/%.2d/%.2d %.2d:%.2d:%.2d.%.3d: ", cur_time.wYear, cur_time.wMonth, cur_time.wDay, cur_time.wHour, cur_time.wMinute, cur_time.wSecond, cur_time.wMilliseconds); ofstream file{ file_path, std::ios::app }; //以追加的方式打开日志文件 file << buff; file << str_text << std::endl; } void CCommon::WriteLog(const wchar_t* str_text, LPCTSTR file_path) { SYSTEMTIME cur_time; GetLocalTime(&cur_time); char buff[32]; sprintf_s(buff, "%d/%.2d/%.2d %.2d:%.2d:%.2d.%.3d: ", cur_time.wYear, cur_time.wMonth, cur_time.wDay, cur_time.wHour, cur_time.wMinute, cur_time.wSecond, cur_time.wMilliseconds); ofstream file{ file_path, std::ios::app }; //以追加的方式打开日志文件 file << buff; file << UnicodeToStr(str_text).c_str() << std::endl; } BOOL CCommon::CreateFileShortcut(LPCTSTR lpszLnkFileDir, LPCTSTR lpszFileName, LPCTSTR lpszLnkFileName, LPCTSTR lpszWorkDir, WORD wHotkey, LPCTSTR lpszDescription, int iShowCmd) { if (lpszLnkFileDir == NULL) return FALSE; HRESULT hr; IShellLink* pLink; //IShellLink对象指针 IPersistFile* ppf; //IPersisFil对象指针 //创建IShellLink对象 hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pLink); if (FAILED(hr)) return FALSE; //从IShellLink对象中获取IPersistFile接口 hr = pLink->QueryInterface(IID_IPersistFile, (void**)&ppf); if (FAILED(hr)) { pLink->Release(); return FALSE; } TCHAR file_path[MAX_PATH]; GetModuleFileName(NULL, file_path, MAX_PATH); //目标 if (lpszFileName == NULL) pLink->SetPath(file_path); else pLink->SetPath(lpszFileName); //工作目录 if (lpszWorkDir != NULL) { pLink->SetWorkingDirectory(lpszWorkDir); } else { //设置工作目录为快捷方式目标所在位置 TCHAR workDirBuf[MAX_PATH]{}; if (lpszFileName == NULL) //wcscpy_s(workDirBuf, file_path); WStringCopy(workDirBuf, 260, file_path, 260); else //wcscpy_s(workDirBuf, lpszFileName); WStringCopy(workDirBuf, 260, lpszFileName); LPTSTR pstr = wcsrchr(workDirBuf, _T('\\')); *pstr = _T('\0'); pLink->SetWorkingDirectory(workDirBuf); } //快捷键 if (wHotkey != 0) pLink->SetHotkey(wHotkey); //备注 if (lpszDescription != NULL) pLink->SetDescription(lpszDescription); //显示方式 pLink->SetShowCmd(iShowCmd); //快捷方式的路径 + 名称 wchar_t szBuffer[MAX_PATH]; if (lpszLnkFileName != NULL) //指定了快捷方式的名称 swprintf_s(szBuffer, L"%s\\%s", lpszLnkFileDir, lpszLnkFileName); else { //没有指定名称,就从取指定文件的文件名作为快捷方式名称。 const wchar_t* pstr; if (lpszFileName != NULL) pstr = wcsrchr(lpszFileName, L'\\'); else pstr = wcsrchr(file_path, L'\\'); if (pstr == NULL) { ppf->Release(); pLink->Release(); return FALSE; } //注意后缀名要从.exe改为.lnk swprintf_s(szBuffer, L"%s\\%s", lpszLnkFileDir, pstr); int nLen = wcslen(szBuffer); szBuffer[nLen - 3] = L'l'; szBuffer[nLen - 2] = L'n'; szBuffer[nLen - 1] = L'k'; } //保存快捷方式到指定目录下 //WCHAR wsz[MAX_PATH]; //定义Unicode字符串 //MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, wsz, MAX_PATH); hr = ppf->Save(szBuffer, TRUE); ppf->Release(); pLink->Release(); return SUCCEEDED(hr); } wstring CCommon::GetStartUpPath() { LPITEMIDLIST ppidl; TCHAR pszStartUpPath[MAX_PATH]{}; if (SHGetSpecialFolderLocation(NULL, CSIDL_STARTUP, &ppidl) == S_OK) { SHGetPathFromIDList(ppidl, pszStartUpPath); CoTaskMemFree(ppidl); } return wstring(pszStartUpPath); } void CCommon::GetFiles(const wchar_t* path, vector& files) { //文件句柄 intptr_t hFile = 0; //文件信息(用Unicode保存使用_wfinddata_t,多字节字符集使用_finddata_t) _wfinddata_t fileinfo; wstring file_name; if ((hFile = _wfindfirst(path, &fileinfo)) != -1) { do { file_name.assign(fileinfo.name); if (file_name != L"." && file_name != L"..") //files.push_back(wstring(path) + L"\\" + file_name); //将文件名保存(忽略"."和"..") files.push_back(L"\\" + file_name); //将文件名保存(忽略"."和"..") } while (_wfindnext(hFile, &fileinfo) == 0); } _findclose(hFile); } void CCommon::GetFiles(const wchar_t* path, std::function func) { //文件句柄 intptr_t hFile = 0; _wfinddata_t fileinfo; wstring file_name; if ((hFile = _wfindfirst(path, &fileinfo)) != -1) { do { file_name.assign(fileinfo.name); if (file_name != L"." && file_name != L"..") func(file_name); } while (_wfindnext(hFile, &fileinfo) == 0); } _findclose(hFile); } bool CCommon::FileExist(LPCTSTR file_name) { return (PathFileExists(file_name) != 0); } bool CCommon::IsFolder(const wstring& path) { DWORD dwAttrib = GetFileAttributes(path.c_str()); return (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0; } bool CCommon::MoveAFile(LPCTSTR exist_file, LPCTSTR new_file) { if (!FileExist(exist_file)) return false; //if (FileExist(new_file)) //如果目标文件已经存在,则先删除它 // DeleteFile(new_file); return (MoveFile(exist_file, new_file) != 0); } SYSTEMTIME CCommon::CompareSystemTime(SYSTEMTIME a, SYSTEMTIME b) { SYSTEMTIME result{}; short hour = a.wHour - b.wHour; short minute = a.wMinute - b.wMinute; short second = a.wSecond - b.wSecond; if (second < 0) { second += 60; minute--; } if (minute < 0) { minute += 60; hour--; } if (hour < 0) { hour += 24; } result.wHour = hour; result.wMinute = minute; result.wSecond = second; return result; } ULONGLONG CCommon::GetCurrentTimeSinceEpochMilliseconds() { FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); // 获取当前系统时间 // 将FILETIME转换为ULARGE_INTEGER以便计算 ULARGE_INTEGER uli; uli.LowPart = fileTime.dwLowDateTime; uli.HighPart = fileTime.dwHighDateTime; // 从1601年1月1日到1970年1月1日的100纳秒间隔数 const ULONGLONG EPOCH_OFFSET = 116444736000000000ULL; // 转换为从1970年1月1日开始的毫秒数 ULONGLONG millisecondsSince1970 = (uli.QuadPart - EPOCH_OFFSET) / 10000; return millisecondsSince1970; } wstring CCommon::GetModuleDir() { wchar_t path[MAX_PATH]; GetModuleFileNameW(NULL, path, MAX_PATH); size_t index; wstring current_path{ path }; index = current_path.find_last_of(L'\\'); current_path = current_path.substr(0, index + 1); return current_path; } wstring CCommon::GetSystemDir() { wchar_t buff[MAX_PATH]; GetSystemDirectory(buff, MAX_PATH); return wstring(buff); } wstring CCommon::GetTemplateDir() { wstring result; wchar_t buff[MAX_PATH]; GetTempPath(MAX_PATH, buff); //获取临时文件夹的路径 result = buff; if (result.back() != L'\\' && result.back() != L'/') //确保路径后面有斜杠 result.push_back(L'\\'); return result; } wstring CCommon::GetAppDataConfigDir() { LPITEMIDLIST ppidl; TCHAR pszAppDataPath[MAX_PATH]; if (SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &ppidl) == S_OK) { SHGetPathFromIDList(ppidl, pszAppDataPath); CoTaskMemFree(ppidl); } wstring app_data_path{ pszAppDataPath }; //获取到C:/User/用户名/AppData/Roaming路径 CreateDirectory(app_data_path.c_str(), NULL); //如果Roaming不存在,则创建它 app_data_path += L"\\TrafficMonitor\\"; CreateDirectory(app_data_path.c_str(), NULL); //如果C:/User/用户名/AppData/Roaming/TrafficMonitor不存在,则创建它 return app_data_path; } void CCommon::DrawWindowText(CDC* pDC, CRect rect, LPCTSTR lpszString, COLORREF color, COLORREF back_color) { pDC->SetTextColor(color); //m_pDC->SetBkMode(TRANSPARENT); //用背景色填充矩形区域 pDC->FillSolidRect(rect, back_color); pDC->DrawText(lpszString, rect, DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); } //void CCommon::SetDrawArea(CDC * pDC, CRect rect) //{ // CRgn rgn; // rgn.CreateRectRgnIndirect(rect); // pDC->SelectClipRgn(&rgn); //} bool CCommon::IsForegroundFullscreen(HMONITOR hMonitor) { if (hMonitor == NULL) hMonitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY); bool bFullscreen{ false }; //用于指示前台窗口是否是全屏 HWND hWnd{}; RECT rcApp{}; // 获取显示器信息 MONITORINFOEX monitorInfo{}; monitorInfo.cbSize = sizeof(monitorInfo); GetMonitorInfo(hMonitor, &monitorInfo); RECT monitorRect = monitorInfo.rcMonitor; hWnd = GetForegroundWindow(); //获取当前正在与用户交互的前台窗口句柄 TCHAR buff[256]; GetClassName(hWnd, buff, 256); //获取前台窗口的类名 CString class_name{ buff }; if (hWnd != GetDesktopWindow() && class_name != _T("WorkerW") && hWnd != GetShellWindow())//如果前台窗口不是桌面窗口,也不是控制台窗口 { GetWindowRect(hWnd, &rcApp); //获取前台窗口的坐标 if (rcApp.left <= monitorRect.left && //如果前台窗口的坐标完全覆盖住桌面窗口,就表示前台窗口是全屏的 rcApp.top <= monitorRect.top && rcApp.right >= monitorRect.right && rcApp.bottom >= monitorRect.bottom) { bFullscreen = true; } }//如果前台窗口是桌面窗口,或者是控制台窗口,就直接返回不是全屏 return bFullscreen; } bool CCommon::CopyStringToClipboard(const wstring& str) { if (OpenClipboard(NULL)) { HGLOBAL clipbuffer; EmptyClipboard(); size_t size = (str.size() + 1) * 2; clipbuffer = GlobalAlloc(GMEM_DDESHARE, size); memcpy_s(GlobalLock(clipbuffer), size, str.c_str(), size); GlobalUnlock(clipbuffer); if (SetClipboardData(CF_UNICODETEXT, clipbuffer) == NULL) return false; CloseClipboard(); return true; } else return false; } wstring CCommon::GetJsonValueSimple(const wstring& json_str, const wstring& name) { wstring str_name{ L"\"" }; str_name += name; str_name += L'\"'; size_t index = json_str.find(str_name); if (index == wstring::npos) return wstring(); index = json_str.find(L':', index + 1); if (index == wstring::npos) return wstring(); index = json_str.find_first_not_of(L"\" ", index + 1); size_t index_end = json_str.find_first_of(L"\",]}\r\n", index); wstring result = json_str.substr(index, index_end - index); return result; } bool CCommon::GetURL(const wstring& url, std::string& result, const wstring& user_agent) { bool succeed{ false }; CInternetSession* pSession{}; CHttpFile* pfile{}; try { pSession = new CInternetSession(user_agent.c_str()); pfile = (CHttpFile*)pSession->OpenURL(url.c_str()); DWORD dwStatusCode; pfile->QueryInfoStatusCode(dwStatusCode); if (dwStatusCode == HTTP_STATUS_OK) { CString content; CString data; while (pfile->ReadString(data)) { content += data; } result = std::string((const char*)content.GetString()); succeed = true; } pfile->Close(); delete pfile; pSession->Close(); } catch (CInternetException* e) { //写入错误日志 if (theApp.m_debug_log) { CString info = CCommon::LoadTextFormat(IDS_GET_URL_ERROR_LOG_INFO, { url, static_cast(e->m_dwError) }); CCommon::WriteLog(info, theApp.m_log_path.c_str()); } if (pfile != nullptr) { pfile->Close(); delete pfile; } if (pSession != nullptr) pSession->Close(); succeed = false; e->Delete(); //没有这句会造成内存泄露 SAFE_DELETE(pSession); } SAFE_DELETE(pSession); return succeed; } bool CCommon::GetURL(const wstring& url, wstring& result, bool utf8, const wstring& user_agent) { std::string str_result; bool succeed = GetURL(url, str_result, user_agent); if (succeed) { result = CCommon::StrToUnicode(str_result.c_str(), utf8); } return succeed; } void CCommon::GetInternetIp(wstring& ip_address, wstring& ip_location, bool global) { wstring web_page; if (GetURL(L"https://ip.cn/", web_page, true)) { #ifdef _DEBUG ofstream file{ L".\\IP_web_page.log" }; file << UnicodeToStr(web_page.c_str()) << std::endl; #endif // _DEBUG size_t index, index1; index = web_page.find(L""); index1 = web_page.find(L"", index + 6); if (index == wstring::npos || index1 == wstring::npos) ip_address.clear(); else ip_address = web_page.substr(index + 6, index1 - index - 6); //获取IP地址 if (ip_address.size() > 15 || ip_address.size() < 7) //IP地址最长15个字符,最短7个字符 ip_address.clear(); //获取IP地址归属地 if (!global) { index = web_page.find(L"", index1 + 7); index1 = web_page.find(L"", index + 6); if (index == wstring::npos || index1 == wstring::npos) ip_location.clear(); else ip_location = web_page.substr(index + 6, index1 - index - 6); } else { index = web_page.find(L"GeoIP", index1 + 7); index1 = web_page.find(L"

", index + 6); if (index == wstring::npos || index1 == wstring::npos) ip_location.clear(); else ip_location = web_page.substr(index + 7, index1 - index - 7); } } else { ip_address.clear(); } } void CCommon::GetInternetIp2(wstring& ip_address, wstring& ip_location, bool ipv6) { wstring raw_string; wstring user_agent{ L"TrafficMonitor/" }; user_agent += VERSION; if (GetURL((ipv6 ? L"https://v6.yinghualuo.cn/bejson" : L"https://v4.yinghualuo.cn/bejson"), raw_string, true, user_agent)) { //解析获取的json字符串 ip_address = GetJsonValueSimple(raw_string, L"ip"); ip_location = GetJsonValueSimple(raw_string, L"location"); } else { ip_address.clear(); ip_location.clear(); } } void CCommon::SetRect(CRect& rect, int x, int y, int width, int height) { rect.left = x; rect.top = y; rect.right = x + width; rect.bottom = y + height; } CString CCommon::LoadText(const wchar_t* id, LPCTSTR back_str) { CString str = theApp.m_str_table.LoadText(id).c_str(); if (back_str != nullptr) str += back_str; return str; } CString CCommon::LoadText(LPCTSTR front_str, const wchar_t* id, LPCTSTR back_str) { CString str = theApp.m_str_table.LoadText(id).c_str(); if (back_str != nullptr) str += back_str; if (front_str != nullptr) str = front_str + str; return str; } CString CCommon::StringFormat(LPCTSTR format_str, const std::initializer_list& paras) { CString str_rtn = format_str; int index = 1; for (const auto& para : paras) { CString para_str = para.ToString(); CString format_para; format_para.Format(_T("<%%%d%%>"), index); str_rtn.Replace(format_para, para_str); index++; } return str_rtn; } CString CCommon::LoadTextFormat(const wchar_t* id, const std::initializer_list& paras) { CString str = theApp.m_str_table.LoadText(id).c_str(); return StringFormat(str.GetString(), paras); } CString CCommon::IntToString(__int64 n, bool thousand_separation, bool is_unsigned) { wstring str = std::to_wstring(is_unsigned ? static_cast(n) : n); int count{}; if (thousand_separation) { int length{ static_cast(str.size()) }; for (int i{ length - 1 }; i > 0; i--) { count++; if (count % 3 == 0) str.insert(i, L","); } } return str.c_str(); } void CCommon::NormalizeFont(LOGFONT& font) { wstring name; wstring style; name = font.lfFaceName; if (name.empty()) return; if (name.back() == L' ') name.pop_back(); size_t index = name.rfind(L' '); if (index == wstring::npos) return; style = name.substr(index + 1); bool style_acquired = false; if (style == L"Light") { font.lfWeight = FW_LIGHT; style_acquired = true; } else if (style == L"Semilight") { font.lfWeight = 350; style_acquired = true; } else if (style == L"Semibold") { font.lfWeight = FW_SEMIBOLD; style_acquired = true; } else if (style == L"Bold") { font.lfWeight = FW_BOLD; style_acquired = true; } else if (style == L"Black") { font.lfWeight = FW_BLACK; style_acquired = true; } if (style_acquired) { name = name.substr(0, index); } //wcsncpy_s(font.lfFaceName, name.c_str(), 32); WStringCopy(font.lfFaceName, 32, name.c_str(), name.size()); } void CCommon::WStringCopy(wchar_t* str_dest, int dest_size, const wchar_t* str_source, int source_size) { if (dest_size <= 0) return; if (source_size <= 0 || str_source == nullptr) { str_dest[0] = L'\0'; return; } int i; for (i = 0; i < dest_size && i < source_size && str_source[i] != L'\0'; i++) str_dest[i] = str_source[i]; //确保目标字符串末尾有一个\0 int copy_cnt = i; if (copy_cnt < dest_size) str_dest[copy_cnt] = L'\0'; else str_dest[dest_size - 1] = L'\0'; } bool CCommon::StringReplace(wstring& str, const wstring& str_old, const wstring& str_new) { if (str.empty()) return false; bool replaced{ false }; size_t pos = 0; while ((pos = str.find(str_old, pos)) != std::wstring::npos) { str.replace(pos, str_old.length(), str_new); replaced = true; pos += str_new.length(); // 前进到替换后的字符串末尾 } return replaced; } template static double _StringSimilarDegree_LD(const T& srcString, const T& matchString) { int n = static_cast(srcString.size()); int m = static_cast(matchString.size()); //int[, ] d = new int[n + 1, m + 1]; // matrix vector> d(n + 1, vector(m + 1)); int cost; // cost // Step 1(如果其中一个字符串长度为0,则相似度为1)? //if (n == 0) return (double)m / max(srcString.size(), matchString.size()); //if (m == 0) return (double)n / max(srcString.size(), matchString.size()); if (n == 0 || m == 0) return 0.0; //如果其中一个字符串长度为0,则相似度为0 // Step 2 for (int i = 0; i <= n; i++) { d[i][0] = i; } for (int j = 0; j <= m; j++) { d[0][j] = j; } // Step 3 for (int i = 1; i <= n; i++) { //Step 4 for (int j = 1; j <= m; j++) { // Step 5 cost = (matchString.substr(j - 1, 1) == srcString.substr(i - 1, 1) ? 0 : 1); // Step 6 d[i][j] = min(min(d[i - 1][j] + 1, d[i][j - 1] + 1), d[i - 1][j - 1] + cost); } } // Step 7 double ds = 1 - (double)d[n][m] / max(srcString.size(), matchString.size()); return ds; } double CCommon::StringSimilarDegree_LD(const std::string& srcString, const std::string& matchString) { return _StringSimilarDegree_LD(srcString, matchString); } double CCommon::StringSimilarDegree_LD(const std::wstring& srcString, const std::wstring& matchString) { return _StringSimilarDegree_LD(srcString, matchString); } void CCommon::SetThreadLanguage(WORD language) { if (language != 0) SetThreadUILanguage(language); } void CCommon::SetColorMode(ColorMode mode) { switch (mode) { case ColorMode::Default: //Win8/8.1下背景色和透明色不使用纯黑色 if (theApp.m_win_version.IsWindows8Or8point1()) { CTrafficMonitorApp::self->m_taskbar_data.dft_back_color = RGB(0, 0, 1); CTrafficMonitorApp::self->m_taskbar_data.dft_transparent_color = RGB(0, 0, 1); } else { CTrafficMonitorApp::self->m_taskbar_data.dft_back_color = 0; CTrafficMonitorApp::self->m_taskbar_data.dft_transparent_color = 0; } CTrafficMonitorApp::self->m_taskbar_data.dft_status_bar_color = 0x005A5A5A; CTrafficMonitorApp::self->m_taskbar_data.dft_text_colors = 0x00ffffffU; CTrafficMonitorApp::self->m_cfg_data.m_dft_notify_icon = 0; break; case ColorMode::Light: CTrafficMonitorApp::self->m_taskbar_data.dft_back_color = 0x00D3D2D2; CTrafficMonitorApp::self->m_taskbar_data.dft_transparent_color = 0x00D3D2D2; CTrafficMonitorApp::self->m_taskbar_data.dft_status_bar_color = 0x00A5A5A5; CTrafficMonitorApp::self->m_taskbar_data.dft_text_colors = 0x00000000U; CTrafficMonitorApp::self->m_cfg_data.m_dft_notify_icon = 4; break; default: break; } } void CCommon::TransparentColorConvert(COLORREF& transparent_color) { if (transparent_color == 0) return; BYTE r = GetRValue(transparent_color); BYTE g = GetGValue(transparent_color); BYTE b = GetBValue(transparent_color); if (r == b) { if (b >= 255) b--; else b++; transparent_color = RGB(r, g, b); } } void CCommon::SetDialogFont(CWnd* pDlg, CFont* pFont) { if (pDlg->GetSafeHwnd() != NULL) { CWnd* pWndChild; pWndChild = pDlg->GetWindow(GW_CHILD); while (pWndChild) { pWndChild->SetFont(pFont); pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); } } } CString CCommon::GetTextResource(UINT id, int code_type) { CString res_str; HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(id), _T("TEXT")); if (hRes != NULL) { DWORD resSize = SizeofResource(NULL, hRes); // 获取资源的大小 HGLOBAL hglobal = LoadResource(NULL, hRes); if (hglobal != NULL) { LPVOID pResourceData = LockResource(hglobal); // 获取资源数据的指针 if (code_type == 2) { // 资源是宽字符字符串 res_str = CString((const wchar_t*)pResourceData, resSize / sizeof(wchar_t)); } else { // 资源是窄字符字符串 std::string strData((const char*)pResourceData, resSize); res_str = CCommon::StrToUnicode(strData.c_str(), (code_type != 0)).c_str(); } } } return res_str; } CString CCommon::GetLastCompileTime() { CString str_compile_time = GetTextResource(IDR_COMPILE_TIME, 0); str_compile_time.Replace(_T("\r\n"), _T("")); str_compile_time.Delete(str_compile_time.GetLength() - 1, 1); return str_compile_time; } HICON CCommon::LoadIconResource(UINT id, int size) { return (HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(id), IMAGE_ICON, size, size, 0); } int CCommon::GetMenuItemPosition(CMenu* pMenu, UINT id) { int pos = -1; int item_count = pMenu->GetMenuItemCount(); for (int i = 0; i < item_count; i++) { if (pMenu->GetMenuItemID(i) == id) { pos = i; break; } } return pos; } // 递归遍历菜单项并处理多语言翻译 static void TranslateMenuItems(CMenu& menu) { // 遍历菜单项 for (int i = 0; i < menu.GetMenuItemCount(); ++i) { UINT menuItemID = menu.GetMenuItemID(i); CString menuText; menu.GetMenuString(i, menuText, MF_BYPOSITION); // 检查菜单项文本是否以TXT_开头 if (menuText.Left(4) == _T("TXT_")) { // 获取翻译后的文本 std::wstring key(menuText); const std::wstring& translatedText = theApp.m_str_table.LoadMenuText(key); // 更新菜单项文本 menu.ModifyMenu(i, MF_BYPOSITION | MF_STRING, menuItemID, translatedText.c_str()); } if (menuItemID == -1) { // 这是一个弹出菜单(子菜单),递归处理 CMenu* pSubMenu = menu.GetSubMenu(i); if (pSubMenu) { TranslateMenuItems(*pSubMenu); // 递归调用 } } } } void CCommon::LoadMenuResource(CMenu& menu, UINT res_id) { // 加载菜单资源 menu.LoadMenu(res_id); // 处理菜单项翻译 TranslateMenuItems(menu); } bool CCommon::IsColorSimilar(COLORREF color1, COLORREF color2) { const int DIFF{ 24 }; return (std::abs(GetRValue(color1) - GetRValue(color2)) < DIFF && std::abs(GetGValue(color1) - GetGValue(color2)) < DIFF && std::abs(GetBValue(color1) - GetBValue(color2)) < DIFF); } int CCommon::CountOneBits(unsigned int value) { int count = 0; while (value != 0) { if (value % 2 == 1) { count++; } value = value >> 1; } return count; } void CCommon::SetNumberBit(unsigned int& num, int bit, bool value) { if (value) { num |= (1 << bit); } else { num &= ~(1 << bit); } } bool CCommon::GetNumberBit(unsigned int num, int bit) { return (num & (1 << bit)) != 0; } COLORREF CCommon::GetWindowsThemeColor() { DWORD crColorization; BOOL fOpaqueBlend; COLORREF theme_color{}; HRESULT result = DwmGetColorizationColor(&crColorization, &fOpaqueBlend); if (result == S_OK) { BYTE r, g, b; r = (crColorization >> 16) % 256; g = (crColorization >> 8) % 256; b = crColorization % 256; theme_color = RGB(r, g, b); } return theme_color; } CString CCommon::GetErrorMessage(DWORD error_code) { CString error_msg; if (error_code != 0) { LPVOID lpMsgBuf = nullptr; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); if (lpMsgBuf != nullptr) error_msg = (LPCTSTR)lpMsgBuf; LocalFree(lpMsgBuf); } return error_msg; } ================================================ FILE: TrafficMonitor/Common.h ================================================ #pragma once #include "CommonData.h" #include "CVariant.h" #include #include class CCommon { public: CCommon(); ~CCommon(); //将const char*字符串转换成宽字符字符串 static wstring StrToUnicode(const char* str, bool utf8 = false); static string UnicodeToStr(const wchar_t* wstr, bool utf8 = false); static wstring AsciiToUnicode(const string& str); static string AsciiToStr(const std::wstring& wstr); static void StringNormalize(std::string& str); static void StringNormalize(std::wstring& str); //将一个字符串分割成若干个字符(模板类型只能为string或wstring) //str: 原始字符串 //div_ch: 用于分割的字符 //result: 接收分割后的结果 static void StringSplit(const std::string& str, char div_ch, vector& results, bool skip_empty = true, bool trim = true); static void StringSplit(const std::wstring& str, wchar_t div_ch, vector& results, bool skip_empty = true, bool trim = true); static void StringSplit(const std::string& str, const std::string& div_str, vector& results, bool skip_empty = true, bool trim = true); static void StringSplit(const std::wstring& str, const std::wstring& div_str, vector& results, bool skip_empty = true, bool trim = true); static bool StringTransform(std::string& str, bool upper); static bool StringTransform(std::wstring& str, bool upper); //读取文件内容 static bool GetFileContent(const wchar_t* file_path, string& contents_buff, bool binary = true); //读取文件内容 //file_path: 文件的路径 //length: 文件的长度 //binary: 是否以进制方式读取 //返回值: 读取成功返回读取到的文件内容的const char类型的指针,在使用完毕后这个指针需要自行使用delete释放。读取失败返回nullptr static const char* GetFileContent(const wchar_t* file_path, size_t& length, bool binary = true); /*根据数据的大小转换成以KB、MB、GB为单位的字符串 size:数据的字节数 返回值:转换后的字符串 */ static CString DataSizeToString(unsigned long long size, const PublicSettingData& cfg); /*根据数据的大小转换成以KB、MB、GB为单位的字符串 size:数据的字节数 with_space:数值和单位是否使用空格分隔 返回值:转换后的字符串 */ static CString DataSizeToString(unsigned long long size, bool with_space = true); //将温度信息转换成字符串 static CString TemperatureToString(float temperature, const PublicSettingData& cfg); //将使用率转换成字符串 static CString UsageToString(int usage, const PublicSettingData& cfg); static CString FreqToString(float usage, const PublicSettingData& cfg); //static CString KBytesToString(unsigned int kb_size); static CString KBytesToString(unsigned __int64 kb_size); //返回两个FILETIME结构的时间差 static __int64 CompareFileTime2(FILETIME time1, FILETIME time2); //将一个日志信息str_text写入到file_path文件中 static void WriteLog(const char* str_text, LPCTSTR file_path); static void WriteLog(const wchar_t* str_text, LPCTSTR file_path); /* 函数功能:对指定文件在指定的目录下创建其快捷方式 函数参数: lpszLnkFileDir 指定目录,不能为NULL。 lpszFileName 指定文件,为NULL表示当前进程的EXE文件。 lpszLnkFileName 快捷方式名称,为NULL表示EXE文件名。 wHotkey 为0表示不设置快捷键 pszDescription 备注 iShowCmd 运行方式,默认为常规窗口 */ static BOOL CreateFileShortcut(LPCTSTR lpszLnkFileDir, LPCTSTR lpszFileName = NULL, LPCTSTR lpszLnkFileName = NULL, LPCTSTR lpszWorkDir = NULL, WORD wHotkey = 0, LPCTSTR lpszDescription = NULL, int iShowCmd = SW_SHOWNORMAL); //获取开始菜单“所有程序”中的“启动”目录的路径 static wstring GetStartUpPath(); //获取path路径下的文件或文件夹,并将文件或文件夹名称保存在files容器中。 static void GetFiles(const wchar_t* path, vector& files); //获取path路径下的文件或文件夹,每次遍历时调用函数对象func //path: 查找的路径 //func: 可以是一个函数对象或lambda表达式,参数是遍历到的文件或文件夹名 static void GetFiles(const wchar_t* path, std::function func); //判断一个文件是否存在 static bool FileExist(LPCTSTR file_name); //判断是否是文件夹 static bool IsFolder(const wstring& path); static bool MoveAFile(LPCTSTR exist_file, LPCTSTR new_file); //计算两个SYSTEMTIME结构时间的差(a-b,只保留时、分、秒) static SYSTEMTIME CompareSystemTime(SYSTEMTIME a, SYSTEMTIME b); //获取从1970年1月1日到现在的毫秒数 static ULONGLONG GetCurrentTimeSinceEpochMilliseconds(); //获取当前程序的目录 static wstring GetModuleDir(); //获取system32文件夹的路径 static wstring GetSystemDir(); //获取临时文件夹的路径 static wstring GetTemplateDir(); //获取Appdata/Local/TrafficMonitor的目录,如果不存在,则会自动创建 static wstring GetAppDataConfigDir(); //在指定位置绘制文本 static void DrawWindowText(CDC* pDC, CRect rect, LPCTSTR lpszString, COLORREF color, COLORREF back_color); ////设置绘图的剪辑区域 //static void SetDrawArea(CDC* pDC, CRect rect); /** * 判断当前正在与用户交互的前台窗口是否是全屏的 * @param[in] hMonitor 要判断的显示器(如果为空,则指定为主显示器) * @return */ static bool IsForegroundFullscreen(HMONITOR hMonitor = NULL); //将一个字符串保存到剪贴板 static bool CopyStringToClipboard(const wstring& str); static wstring GetJsonValueSimple(const wstring& json_str, const wstring& name); //获取URL的内容 static bool GetURL(const wstring& url, std::string& result, const wstring& user_agent = wstring()); static bool GetURL(const wstring& url, wstring& result, bool utf8 = false, const wstring& user_agent = wstring()); //获取外网IP地址和IP归属地 static void GetInternetIp(wstring& ip_address, wstring& ip_location, bool global); static void GetInternetIp2(wstring& ip_address, wstring& ip_location, bool ipv6); static void SetRect(CRect& rect, int x, int y, int width, int height); //从资源文件载入字符串。其中,front_str、back_str为载入字符串时需要在前面或后面添加的字符串 static CString LoadText(const wchar_t* id, LPCTSTR back_str = nullptr); static CString LoadText(LPCTSTR front_str, const wchar_t* id, LPCTSTR back_str); //安全的格式化字符串,将format_str中形如<%序号%>的字符串替换成初始化列表paras中的元素,元素支持int/double/LPCTSTR/CString格式,序号从1开始 static CString StringFormat(LPCTSTR format_str, const std::initializer_list& paras); //从资源文件中载入字符串,并将资源字符串中形如<%序号%>的字符串替换成可变参数列表中的参数 static CString LoadTextFormat(const wchar_t* id, const std::initializer_list& paras); //将int类型转换成字符串 //n:要转换的数值 //thousand_separation:是否要每隔3位数使用逗号分隔 //is_unsigned:数值是否是无符号的 static CString IntToString(__int64 n, bool thousand_separation = false, bool is_unsigned = false); //删除字体名称后面的Bold、Light等字符串,并根据这些字符串设置字体粗细 static void NormalizeFont(LOGFONT& font); //安全的字符串复制函数 static void WStringCopy(wchar_t* str_dest, int dest_size, const wchar_t* str_source, int source_size = INT_MAX); //字符串替换 static bool StringReplace(wstring& str, const wstring& str_old, const wstring& str_new); /// /// 字符串相似度算法-编辑距离法 /// /// 返回的值为0~1,越大相似度越高 static double StringSimilarDegree_LD(const std::string& srcString, const std::string& matchString); static double StringSimilarDegree_LD(const std::wstring& srcString, const std::wstring& matchString); //设置线程语言 static void SetThreadLanguage(WORD language); //设置颜色模式 static void SetColorMode(ColorMode mode); //经过测试发现,似乎当透明色的R和B值相等时,会出现右击任务栏窗口时无法弹出右键菜单,而是弹出系统任务栏右键菜单的问题 //为了解决这个问题,需要将颜色值进行转换 //此函数的作用是判断一个颜色的R和B值是否相等,如果是则将颜色的B值加1(如果B==255,则减1) static void TransparentColorConvert(COLORREF& transparent_color); static void SetDialogFont(CWnd* pDlg, CFont* pFont); //从资源加载自定义文本资源。id:资源的ID,code_type:文本的编码格式:0:ANSI, 1:UTF8, 2:UTF16 static CString GetTextResource(UINT id, int code_type); //从资源文件读取上次编译时间 static CString GetLastCompileTime(); //从资源加载一个图标 static HICON LoadIconResource(UINT id, int size); //获取一个菜单项的序号 static int GetMenuItemPosition(CMenu* pMenu, UINT id); //从资源文件加载一个菜单,并处理文本翻译 static void LoadMenuResource(CMenu& menu, UINT res_id); static bool IsColorSimilar(COLORREF color1, COLORREF color2); //计算二进制中1的个数 static int CountOneBits(unsigned int value); //设置一个数字的某个bit位 static void SetNumberBit(unsigned int& num, int bit, bool value); //获取一个数字的某个bit位 static bool GetNumberBit(unsigned int num, int bit); //在不排序的情况下删除vector中的重复元素 template static void RemoveVectorDuplicateItem(vector& vec) { std::set si; for (auto it = vec.begin(); it != vec.end();) { if (si.count(*it) == 0)//这里判断当前元素是否已经出现过,若没有出现过则将该元素保留,并做标记 { si.insert(*it); ++it; } else//如果前面已经出现过,则删除当前元素 { it = vec.erase(it);//这里删除it指向的元素 } } } /** * @brief 限制一个数的范围 * @param value 要限制范围的值 * @param min_value 最小值 * @param max_value 最大值 */ template static void ValidatValue(T& value, const T& min_value, const T& max_value) { if (value < min_value) value = min_value; if (value > max_value) value = max_value; } //获取Windows主题颜色 static COLORREF GetWindowsThemeColor(); static CString GetErrorMessage(DWORD error_code); }; /** * @brief 编译期获取数组长度 * * @tparam T 数组元素类型 * @tparam N 编译期推断的数组长度 * @return constexpr std::size_t 编译期推断的数组长度 */ template constexpr std::size_t GetArrayLength(const T (&)[N]) noexcept { return N; } /** * @brief 析构StaticVariableWrapper包装对象前默认执行的函数,实际上无操作 * * @tparam T */ template class CDefaultStaticVariableWrapperDtor { public: void operator()(T*){}; }; /** * @brief 设计上用于静态变量包装类,用于自定义变量默认初始化后行为和析构前行为 * * @tparam T 要被包装的类型 * @tparam DTOR 自定义执行析构函数前的行为 */ template > class CStaticVariableWrapper : private DTOR { private: T m_content; public: /** * @brief 构造一个StaticVariableWrapper * * @tparam CTOR 自定义变量默认初始化后的函数类型 * @param ctor 自定义变量默认初始化后的行为,传入变量的指针作为参数 * @param dtor 自定义变量执行析构函数前的行为,传入变量的指针作为参数 */ template CStaticVariableWrapper(CTOR ctor, DTOR dtor = {}) : DTOR{dtor} { ctor(std::addressof(m_content)); } ~CStaticVariableWrapper() { (*static_cast(this))(std::addressof(m_content)); } T& Get() noexcept { return m_content; } const T& Get() const noexcept { return m_content; } }; /** * @brief 生成静态变量包装类的函数 * * @tparam T 要被包装的类型 * @tparam CTOR 自定义变量默认初始化后的函数类型 * @tparam DTOR 自定义变量执行析构函数前的函数类型 * @param ctor 自定义变量默认初始化后的行为,传入变量的指针作为参数 * @param dtor 自定义变量执行析构函数前的行为,传入变量的指针作为参数 * @return CStaticVariableWrapper 包装后的变量,已经初始化 */ template > auto MakeStaticVariableWrapper(CTOR ctor, DTOR dtor = {}) -> CStaticVariableWrapper { return {ctor, dtor}; } /** * @brief 调用指针指向的对象的对应类型的析构函数 * * @tparam T 传入的移除了指针后的类型 * @param p_memory 指向要执行析构函数的对象的指针 */ template void Destroy(T* p_memory) { p_memory->~T(); } template void EmplaceAt(T* p_memory, Args&&... args) { ::new (p_memory) T(std::forward(args)...); } ================================================ FILE: TrafficMonitor/CommonData.cpp ================================================ #include "stdafx.h" #include "CommonData.h" #include "Common.h" #include "CalendarHelper.h" #include "TrafficMonitor.h" #include "WindowsSettingHelper.h" /////////////////////////////////////////////////////////////////////////////////// int Date::week() const { //计算当前是一年的第几天 int days{}; for (int i{ 1 }; i < month; i++) { days += CCalendarHelper::DaysInMonth(year, i); } days += day; //计算这一年的1月1日是星期几 int week_day = CCalendarHelper::CaculateWeekDay(year, 1, 1); if (theApp.m_cfg_data.m_sunday_first) { days += (week_day - 1); } else { days += (week_day - 2); } return days / 7 + 1; } bool Date::DateGreater(const Date& a, const Date& b) { if (a.year != b.year) return a.year > b.year; else if (a.month != b.month) return a.month > b.month; else if (a.day != b.day) return a.day > b.day; else return false; } bool Date::DateEqual(const Date& a, const Date& b) { return a.year == b.year && a.month == b.month && a.day == b.day; } /////////////////////////////////////////////////////////////////////////////////// //HistoryTraffic unsigned __int64 HistoryTraffic::kBytes() const { return up_kBytes + down_kBytes; } /////////////////////////////////////////////////////////////////////////////////// wstring& DispStrings::Get(CommonDisplayItem item) { return map_str[item]; } const wstring& DispStrings::GetConst(CommonDisplayItem item) const { auto iter = map_str.find(item); if (iter != map_str.end()) return iter->second; static wstring empty_str; return empty_str; } const std::map& DispStrings::GetAllItems() const { return map_str; } bool DispStrings::operator==(const DispStrings& disp_str) const { return map_str == disp_str.map_str; } bool DispStrings::IsInvalid() const { return map_str.empty(); } void DispStrings::Load(const std::wstring& plugin_id, const std::wstring& disp_str) { auto plugin = theApp.m_plugins.GetItemById(plugin_id); if (plugin != nullptr) { map_str[plugin] = disp_str; } } /////////////////////////////////////////////////////////////////////////////////// bool FontInfo::operator==(const FontInfo& a) const { return name == a.name && size == a.size && bold == a.bold && italic == a.italic && underline == a.underline && strike_out == a.strike_out; } /////////////////////////////////////////////////////////////////////////////////// bool StringSet::Contains(const std::wstring& str) const { return string_set.count(str) != 0; } void StringSet::SetStrContained(const std::wstring& str, bool contained) { if (contained) string_set.insert(str); else string_set.erase(str); } void StringSet::FromString(const std::wstring& str) { std::vector item_vect; CCommon::StringSplit(str, L',', item_vect); string_set.clear(); for (const auto& i : item_vect) string_set.insert(i); } std::wstring StringSet::ToString() const { std::vector item_vect; for (const auto& i : string_set) item_vect.push_back(i); std::wstring item_str; for (const auto& i : item_vect) { item_str += i; item_str += L','; } if (!item_str.empty()) item_str.pop_back(); return item_str; } void StringSet::FromVector(const std::vector& vec) { string_set.clear(); for (const auto& str : vec) string_set.insert(str); } std::vector StringSet::ToVector() const { std::vector vec; for (const auto& str : string_set) vec.push_back(str); return vec; } std::set& StringSet::data() { return string_set; } /////////////////////////////////////////////////////////////////////////////////// bool SkinSettingData::IsEmpty() const { return font.name.IsEmpty() && disp_str.GetAllItems().empty() && text_colors.empty(); } bool SkinSettingData::operator==(const SkinSettingData& a) const { return font == a.font && disp_str == a.disp_str && text_colors == a.text_colors && specify_each_item_color == a.specify_each_item_color; } /////////////////////////////////////////////////////////////////////////////////// void MainWndSettingData::FormSkinSettingData(const SkinSettingData& sking_setting_data) { font = sking_setting_data.font; disp_str = sking_setting_data.disp_str; text_colors = sking_setting_data.text_colors; specify_each_item_color = sking_setting_data.specify_each_item_color; } SkinSettingData MainWndSettingData::ToSkinSettingData() const { SkinSettingData sking_setting_data; sking_setting_data.font = font; sking_setting_data.disp_str = disp_str; sking_setting_data.text_colors = text_colors; sking_setting_data.specify_each_item_color = specify_each_item_color; return sking_setting_data; } /////////////////////////////////////////////////////////////////////////////////// bool TaskBarSettingData::IsTaskbarTransparent() const { if (CWindowsSettingHelper::IsWindows10LightTheme() || theApp.m_win_version.IsWindows8Or8point1() || theApp.IsWindows11Taskbar()) return (transparent_color == back_color); else return transparent_color == 0; } void TaskBarSettingData::SetTaskabrTransparent(bool transparent) { if (transparent) { if (CWindowsSettingHelper::IsWindows10LightTheme() || theApp.m_win_version.IsWindows8Or8point1() || theApp.IsWindows11Taskbar()) { //浅色模式下要设置任务栏窗口透明,只需将透明色设置成和背景色一样即可 CCommon::TransparentColorConvert(back_color); transparent_color = back_color; } else { //深色模式下,背景色透明将透明色设置成黑色 transparent_color = 0; } } else { //要设置任务栏窗口不透明,只需将透明色设置成和背景色不一样即可 if (back_color != TASKBAR_TRANSPARENT_COLOR1) transparent_color = TASKBAR_TRANSPARENT_COLOR1; else transparent_color = TASKBAR_TRANSPARENT_COLOR2; } } void TaskBarSettingData::ValidItemSpace() { if (item_space < 0) item_space = 0; if (item_space > 32) item_space = 32; } void TaskBarSettingData::ValidVerticalMargin() { if (vertical_margin < -10) vertical_margin = -10; if (vertical_margin > 10) vertical_margin = 10; } void TaskBarSettingData::ValidWindowOffsetTop() { if (window_offset_top < -20) window_offset_top = -20; if (window_offset_top > 20) window_offset_top = 20; } void TaskBarSettingData::ValidWindowOffsetLeft() { if (window_offset_left < -800) window_offset_top = -800; if (window_offset_top > 800) window_offset_top = 800; } unsigned __int64 TaskBarSettingData::GetNetspeedFigureMaxValueInBytes() const { if (netspeed_figure_max_value_unit == 0) //单位为KB return static_cast(netspeed_figure_max_value) * 1024; else return static_cast(netspeed_figure_max_value) * 1024 * 1024; } COLORREF TaskBarSettingData::GetUsageGraphColor() const { if (graph_color_following_system) { COLORREF theme_color = theApp.GetThemeColor(); //转换为HLS double h, l, s; CDrawingManager::RGBtoHSL(theme_color, &h, &s, &l); //根据当前系统深浅色模式指定亮度 if (theApp.m_last_light_mode) { //浅色任务栏,将亮度设为0.7 l = 0.7; } else { //深色任务栏,将亮度设为0.4 l = 0.4; } //转换回RGB COLORREF graph_color = CDrawingManager::HLStoRGB_ONE(h, l, s); return graph_color; } else { return status_bar_color; } } ================================================ FILE: TrafficMonitor/CommonData.h ================================================ //此文件包含全局结构体、枚举类型的定义 #pragma once #include "stdafx.h" #include "TaskbarItemOrderHelper.h" struct Date { int year{}; int month{}; int day{}; int week() const; //该日期是一年的第几周 //比较两个HistoryTraffic对象的日期,如果a的时间大于b,则返回true static bool DateGreater(const Date& a, const Date& b); //判断两个HistoryTraffic对象的日期是否相等 static bool DateEqual(const Date& a, const Date& b); }; //储存某一天的历史流量 struct HistoryTraffic : public Date { //当天使用的流量(以KB为单位) unsigned __int64 up_kBytes{}; unsigned __int64 down_kBytes{}; bool mixed{ true }; //如果不区分上传和下载流量,则为true unsigned __int64 kBytes() const; }; //历史流量统计中用于指示不同范围内的流量的颜色 #define TRAFFIC_COLOR_BLUE RGB(0, 183, 238) #define TRAFFIC_COLOR_GREEN RGB(128, 194, 105) #define TRAFFIC_COLOR_YELLOE RGB(255, 216, 58) #define TRAFFIC_COLOR_RED RGB(255, 95, 74) #define TRAFFIC_COLOR_DARK_RED RGB(166, 19, 0) //网速单位 enum class SpeedUnit { AUTO, //自动 KBPS, //KB/s MBPS //MB/s }; //硬件监控的项目 enum HardwareItem { HI_CPU = 1 << 0, //CPU HI_GPU = 1 << 1, //显卡 HI_HDD = 1 << 2, //硬盘 HI_MBD = 1 << 3 //主板 }; #define DEF_CH L'\"' //写入和读取ini文件字符串时,在字符串前后添加的字符 struct DispStrings //显示的文本 { private: std::map map_str; public: //获取一个显示的文本 wstring& Get(CommonDisplayItem item); const wstring& GetConst(CommonDisplayItem item) const; const std::map& GetAllItems() const; bool operator==(const DispStrings& disp_str) const; //载入一个插件项目的显示文本 void Load(const std::wstring& plugin_id, const std::wstring& disp_str); //是否无效 bool IsInvalid() const; }; //鼠标双击窗口的动作 enum class DoubleClickAction { CONNECTION_INFO, //连接详情 HISTORY_TRAFFIC, //历史流量统计 SHOW_MORE_INFO, //显示更多信息 OPTIONS, //选项设置 TASK_MANAGER, //任务管理器 SEPCIFIC_APP, //指定应用程序 CHANGE_SKIN, //更换皮肤 NONE //不执行任何动作 }; //颜色模式 enum class ColorMode { Default, //默认颜色 Light //浅色 }; //将字号转成LOGFONT结构中的lfHeight inline int FontSizeToLfHeight(int font_size, int dpi = 0) { if (dpi == 0) { HDC hDC = ::GetDC(HWND_DESKTOP); dpi = GetDeviceCaps(hDC, LOGPIXELSY); ::ReleaseDC(HWND_DESKTOP, hDC); } int lfHeight = -MulDiv(font_size, dpi, 72); return lfHeight; } //字体 struct FontInfo { CString name; //字体名称 int size{ 9 }; //字体大小 bool bold{}; //粗体 bool italic{}; //斜体 bool underline{}; //下划线 bool strike_out{}; //删除线 bool operator==(const FontInfo& a) const; //创建一个CFont对象 void Create(CFont& font, int dpi = 0) const { font.CreateFont( FontSizeToLfHeight(size, dpi), // nHeight 0, // nWidth 0, // nEscapement 0, // nOrientation (bold ? FW_BOLD : FW_NORMAL), // nWeight italic, // bItalic underline, // bUnderline strike_out, // cStrikeOut DEFAULT_CHARSET, // nCharSet OUT_DEFAULT_PRECIS, // nOutPrecision CLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQuality DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily name); } }; //历史流量统计列表视图中显示模式 enum class HistoryTrafficViewType { HV_DAY, //日视图 HV_WEEK, //周视图 HV_MONTH, //月视图 HV_QUARTER, //季视图 HV_YEAR //年视图 }; struct StringSet { public: bool Contains(const std::wstring& str) const; void SetStrContained(const std::wstring& str, bool contained); void FromString(const std::wstring& str); std::wstring ToString() const; void FromVector(const std::vector& vec); std::vector ToVector() const; std::set& data(); private: std::set string_set; }; //选项设置数据 struct MainConfigData { int m_transparency{ 100 }; //窗口透明度 bool m_show_more_info{ false }; //显示更多信息 bool m_show_task_bar_wnd{ false }; //显示任务栏窗口 bool m_hide_main_window; //隐藏主窗口 //bool m_tbar_show_cpu_memory; //任务栏窗口显示CPU和内存利用率 int m_position_x; //窗口位置的x坐标 int m_position_y; //窗口位置的y坐标 bool m_auto_select{ false }; //自动选择连接 bool m_select_all{ false }; //统计所有连接的网速 string m_connection_name; //当前选择网络的名称 wstring m_skin_name; //选择的皮肤的名称 bool skin_auto_adapt{ false }; //根据深色/浅色模式自动切换皮肤 wstring skin_name_light_mode; //浅色模式下的皮肤名称 wstring skin_name_dark_mode; //深色模式下的皮肤名称 int m_dft_notify_icon = 0; //默认的通知图标(用于区分win10的深色和浅色模式) int m_notify_icon_selected{}; //要显示的通知区图标 bool m_notify_icon_auto_adapt{ false }; //通知区图标是否自动适应Win10深浅色模式 //bool m_show_internet_ip{ false }; //是否在“连接详情”对话框中显示外网IP地址 bool m_use_log_scale{ false }; //“历史流量统计”对话框中绘制表示历史流量数值的矩形时是否使用对数比例 HistoryTrafficViewType m_view_type{}; bool m_sunday_first{ true }; //是否将周日作为一周的第一天 StringSet plugin_disabled; //已禁用的插件 }; //内存显示方式 enum class MemoryDisplay { USAGE_PERCENTAGE, //已使用百分比 MEMORY_USED, //内存已使用 MEMORY_AVAILABLE //内存可用 }; //为每个皮肤单独保存的数据 struct SkinSettingData { FontInfo font; //字体 DispStrings disp_str; //显示的文本 std::map text_colors{}; //文字的颜色 bool specify_each_item_color{}; bool IsEmpty() const; bool operator==(const SkinSettingData& a) const; }; //选项设置中“主窗口设置”和“任务栏窗口设置”中公共的数据(不使用此结构体创建对象) struct PublicSettingData { bool specify_each_item_color{ false }; //是否指定每个项目的颜色 FontInfo font; //字体 DispStrings disp_str; //显示的文本 bool speed_short_mode{ false }; //网速显示简洁模式(减少小数点的位数,单位不显示“B”) bool separate_value_unit_with_space{ true }; //网速数值和单位用空格分隔 bool show_tool_tip{ true }; //显示鼠标提示 MemoryDisplay memory_display{ MemoryDisplay::USAGE_PERCENTAGE }; //内存显示方式 bool unit_byte{ true }; //使用字节(B)而不是比特(b)为单位 SpeedUnit speed_unit; //网速的单位 bool hide_unit; //隐藏单位 bool hide_percent; //隐藏百分号 DoubleClickAction double_click_action; //鼠标双击动作 wstring double_click_exe; //鼠标双击动作为打开指定应用程序时,打开的程序路径 }; //#define MAIN_WND_COLOR_NUM 9 //主窗口颜色数量 //选项设置中“主窗口设置”的数据 struct MainWndSettingData : public PublicSettingData { std::map text_colors{}; //文字的颜色 bool swap_up_down{ false }; //交换上传和下载显示的位置 bool hide_main_wnd_when_fullscreen; //有程序全屏运行时隐藏悬浮窗 bool m_always_on_top{ false }; //窗口置顶 bool m_lock_window_pos{ false }; //锁定窗口位置 bool m_mouse_penetrate{ false }; //鼠标穿透 bool m_alow_out_of_border{ false }; //是否允许悬浮窗超出屏幕边界 void FormSkinSettingData(const SkinSettingData& sking_setting_data); SkinSettingData ToSkinSettingData() const; }; //#define TASKBAR_COLOR_NUM 18 //任务栏窗口颜色数量 struct TaskbarItemColor //任务栏窗口每一项的颜色 { COLORREF label{}; //标签颜色 COLORREF value{}; //数值颜色 bool operator==(const TaskbarItemColor& item) const { return label == item.label && value == item.value; } }; //选项设置中“任务栏窗口设置”的数据 struct TaskBarSettingData : public PublicSettingData { COLORREF back_color{ RGB(0, 0, 0) }; //背景颜色 COLORREF transparent_color{ RGB(0, 0, 0) }; //透明色 COLORREF status_bar_color{ RGB(0, 0, 0) }; // CPU/内存 状态条颜色 std::map text_colors{}; //文字的颜色 int dft_back_color = 0; //默认背景颜色 int dft_transparent_color = 0; //默认透明色 int dft_status_bar_color = 0x005A5A5A; //默认CPU/内存 状态条颜色 int dft_text_colors = 0x00ffffffU; //默认文字颜色 bool auto_adapt_light_theme{ true }; //是否自动适应浅色主题 int dark_default_style{ 0 }; //深色主题时使用的预设方案 int light_default_style{ -1 }; //浅色主题时使用的预设方案 bool auto_set_background_color{ false }; //根据任务栏颜色自动设置背景色 bool auto_save_taskbar_color_settings_to_preset{}; //当启用“自动适应Windows10深色/浅色主题”时,是否在颜色设置有更改时自动将当前颜色设置保存到对应的预设 bool IsTaskbarTransparent() const; void SetTaskabrTransparent(bool transparent); CTaskbarItemOrderHelper item_order; DisplayItemSet display_item{ TDI_UP, TDI_DOWN }; //任务栏窗口显示的项目 StringSet plugin_display_item; //任务窗口显示的插件项目 bool show_taskbar_wnd_in_secondary_display{ false }; //是否在副显示器上显示任务栏窗口 int secondary_display_index{}; //在第几个副显示器上显示任务栏窗口 bool value_right_align{ false }; //数值是否右对齐 int digits_number{ 4 }; //数据位数 bool horizontal_arrange{ true }; //水平排列 bool show_status_bar{ true }; //显示 CPU/内存的状态条 bool tbar_wnd_on_left{ false }; //如果为true,则任务栏窗口显示在任务栏的左侧(或上方) bool tbar_wnd_snap{ false }; //如果为true,则在Win11中任务栏窗口贴靠中间任务栏,否则靠近边缘 bool cm_graph_type{ false }; //如果为false,默认原样式,柱状图显示占用率,如为true,滚动显示占用率 bool show_graph_dashed_box{ true }; //显示占用图虚线框 int item_space{}; //项目间距 int vertical_margin{}; //项目垂直间距 int window_offset_top{}; //任务栏窗口顶部边距 int window_offset_left{}; //任务栏窗口左侧边距 void ValidItemSpace(); void ValidVerticalMargin(); void ValidWindowOffsetTop(); void ValidWindowOffsetLeft(); bool avoid_overlap_with_widgets{ false }; //避免与右侧小组件重叠 int taskbar_left_space_win11{}; //Windows11下,任务栏小工具的宽度 int taskbar_right_space_win11{}; //Windows11下,任务栏窗口距离任务栏右侧的宽度(仅当无法获取到任务栏TrayNotifyWnd窗口的位置时有效) bool show_netspeed_figure{ false }; //是否显示网速占用图 int netspeed_figure_max_value; //网速占用图的最大值 int netspeed_figure_max_value_unit{}; //网速占用图最大值的单位(0: KB, 1: MB) unsigned __int64 GetNetspeedFigureMaxValueInBytes() const; //获取网速占用图的最大值(以字节为单位) bool graph_color_following_system{ false }; //占用图颜色跟随系统主题色 COLORREF GetUsageGraphColor() const; //获取占用图的颜色 bool disable_d2d{ false };//是否禁用d2d绘图 DWORD update_layered_window_error_code{0}; // 使用D2D1渲染时,UpdateLayeredWindowIndirect失败的错误代码,会在关闭任务栏窗口时被重置为0 bool enable_colorful_emoji{ true }; //是否显示彩色emoji bool is_windows_web_experience_detected{ false }; //是否检测到Windows Web Experience小组件安装信息 }; //选项设置中“常规设置”的数据 struct GeneralSettingData { bool check_update_when_start{ true }; int update_source{}; //更新源。0: GitHub; 1: Gitee bool auto_run{ false }; bool show_notify_icon{ true }; //显示通知区域图标 //通知消息 bool traffic_tip_enable{ false }; //是否启用流量超出时提示 int traffic_tip_value; //要提示的流量临界值 int traffic_tip_unit{}; //要提示的流量值的单位(0: MB, 1: GB) struct NotifyTipSettings //超过某个值时弹出提示的设置 { bool enable; //是否启用提示 int tip_value; //要弹出提示的临界值 }; NotifyTipSettings memory_usage_tip; //用内存使用率超出提示 NotifyTipSettings cpu_temp_tip; //CPU温度超出提示 NotifyTipSettings gpu_temp_tip; //显卡温度超出提示 NotifyTipSettings hdd_temp_tip; //硬盘温度超出提示 NotifyTipSettings mainboard_temp_tip; //主板温度超出提示 //语言id WORD language; bool show_all_interface{ true }; //CPU利用率获取方式 enum CpuUsageAcquireMethod { CA_CPU_TIME, //使用时间 CA_PDH, //性能计数器 CA_HARDWARE_MONITOR //来自硬件监控 }; CpuUsageAcquireMethod cpu_usage_acquire_method{}; //获取CPU利用率的方式 bool portable_mode{ false }; //便携模式,如果为true,则程序所有数据都保存到exe所在目录下,否则保存到Appdata\Romaing目录下 int monitor_time_span{ 1000 }; //监控的时间间隔 std::wstring hard_disk_name; //要监控的硬盘名称 std::wstring cpu_core_name; //要监控的CPU核心的名称 unsigned int hardware_monitor_item{}; //要监控哪些硬件 bool IsHardwareEnable(HardwareItem item_type) const { return hardware_monitor_item & item_type; } void SetHardwareEnable(HardwareItem item_type, bool enable) { if (enable) hardware_monitor_item |= item_type; else hardware_monitor_item &= ~item_type; } StringSet connections_hide; //用于保存哪些网络要从“选择网络连接”子菜单项中隐藏 }; //定义监控时间间隔有效的最大值和最小值 #define MONITOR_TIME_SPAN_MIN 200 #define MONITOR_TIME_SPAN_MAX 30000 //通过构造函数传递一个bool变量的引用,在构造时将其置为true,析构时置为false class CFlagLocker { public: CFlagLocker(bool& flag) : m_flag(flag) { m_flag = true; } ~CFlagLocker() { m_flag = false; } private: bool& m_flag; }; ================================================ FILE: TrafficMonitor/D2D1Support.cpp ================================================ #include "stdafx.h" #include "D2D1Support.h" #include "Common.h" #pragma comment(lib, "D2d1.lib") #pragma comment(lib, "Dwrite.lib") bool CD2D1Support::CheckSupport() { static bool result = FunctionChecker::CheckFunctionExist(_T("D2d1.dll"), "D2D1CreateFactory"); return result; } ID2D1Factory* CD2D1Support::GetFactory() { static auto result = MakeStaticVariableWrapper( [](ID2D1Factory** pp_factory) { *pp_factory = nullptr; ThrowIfFailed(::D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, CD2D1Support::CREATION_OPTIONS, pp_factory), TRAFFICMONITOR_ERROR_STR("Create D2D1 factory failed.")); }, [](ID2D1Factory** pp_factory) { auto* p_factory = *pp_factory; RELEASE_COM(p_factory); }); return result.Get(); } bool CD2D1Support1::CheckSupport() { static bool result = FunctionChecker::CheckFunctionExist(_T("D2D1.dll"), "D2D1CreateDevice"); return result; } ID2D1Factory1* CD2D1Support1::GetFactory() { static auto result = MakeStaticVariableWrapper( [](auto pp_factory) { *pp_factory = nullptr; ThrowIfFailed(::D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, CD2D1Support::CREATION_OPTIONS, pp_factory), TRAFFICMONITOR_ERROR_STR("Create D2D1 factory failed.")); }, [](auto pp_factory) { auto* p_factory = *pp_factory; RELEASE_COM(p_factory); }); return result.Get(); } void CD2D1Device::Recreate(Microsoft::WRL::ComPtr p_dxgi_device) { ThrowIfFailed( CD2D1Support1::GetFactory()->CreateDevice( p_dxgi_device.Get(), &m_p_device), TRAFFICMONITOR_ERROR_STR("Create ID2D1Device failed.")); NotifyAllResourceWhenDeviceRecreate( m_resource_tracker, m_p_device); } auto CD2D1Device::GetStorage() -> std::shared_ptr { return m_resource_tracker.GetSharedResourceTrackerStorage(); } auto CD2D1Device::Get() -> Type { return m_p_device; } bool CDWriteSupport::CheckSupport() { static bool result = FunctionChecker::CheckFunctionExist(_T("Dwrite.dll"), "DWriteCreateFactory"); return result; } IDWriteFactory* CDWriteSupport::GetFactory() { static auto result = MakeStaticVariableWrapper( [](IDWriteFactory** pp_factory) { *pp_factory = nullptr; ThrowIfFailed(::DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(pp_factory)), TRAFFICMONITOR_ERROR_STR("Create DWrite factory failed.")); }, [](IDWriteFactory** pp_factory) { auto* p_factory = *pp_factory; RELEASE_COM(p_factory); }); return result.Get(); } ================================================ FILE: TrafficMonitor/D2D1Support.h ================================================ #pragma once #include #include #include "HResultException.h" #include "RenderAPISupport.h" class CD2D1Exception final : public CHResultException { using CHResultException::CHResultException; }; class CDWriteException final : public CHResultException { using CHResultException::CHResultException; }; class CD2D1Support { public: #ifdef DEBUG constexpr static D2D1_FACTORY_OPTIONS CREATION_OPTIONS{D2D1_DEBUG_LEVEL_INFORMATION}; #else constexpr static D2D1_FACTORY_OPTIONS CREATION_OPTIONS{D2D1_DEBUG_LEVEL_NONE}; #endif static bool CheckSupport(); static ID2D1Factory* GetFactory(); }; class CD2D1Device { public: using Type = Microsoft::WRL::ComPtr; using Resource = CDeviceResource; using Storage = storage_t>; private: Type m_p_device{}; CResourceTracker m_resource_tracker{std::make_shared()}; public: void Recreate(Microsoft::WRL::ComPtr p_dxgi_device); auto GetStorage() -> std::shared_ptr; Type Get(); }; class CD2D1Support1 { public: static bool CheckSupport(); static ID2D1Factory1* GetFactory(); }; class CDWriteSupport { public: static bool CheckSupport(); static IDWriteFactory* GetFactory(); }; ================================================ FILE: TrafficMonitor/D3D10Support1.cpp ================================================ #include "stdafx.h" #include #include #include #include #include "D3D10Support1.h" #include "Common.h" #include "Nullable.hpp" #include "DllFunctions.h" #pragma comment(lib, "DXGI.lib") #pragma comment(lib, "D3D10_1.lib") using Microsoft::WRL::ComPtr; void CD3D10Device1::Recreate(Microsoft::WRL::ComPtr p_adapter1) { m_sp_storage->m_p_adapter1 = p_adapter1; ThrowIfFailed( D3D10CreateDevice1( m_sp_storage->m_p_adapter1.Get(), m_sp_storage->m_driver_type, m_sp_storage->m_h_software, m_sp_storage->m_flags, m_sp_storage->m_feature_level, m_sp_storage->m_sdk_version, &m_sp_storage->m_p_device1), TRAFFICMONITOR_ERROR_STR("Create D3D10Device1 failed.")); NotifyAllResourceWhenDeviceRecreate( m_resource_tracker, m_sp_storage->m_p_device1); } auto CD3D10Device1::Get() noexcept -> Microsoft::WRL::ComPtr { return m_sp_storage->m_p_device1; } auto CD3D10Device1::GetAdapter() noexcept -> Microsoft::WRL::ComPtr { return m_sp_storage->m_p_adapter1; } auto CD3D10Device1::GetDriverType() const noexcept -> D3D10_DRIVER_TYPE { return m_sp_storage->m_driver_type; } auto CD3D10Device1::SetDriverType(D3D10_DRIVER_TYPE driver_type) noexcept -> CD3D10Device1& { m_sp_storage->m_driver_type = driver_type; return *this; } auto CD3D10Device1::GetSoftwareHandle() const noexcept -> HMODULE { return m_sp_storage->m_h_software; } auto CD3D10Device1::SetSoftwareHandle(HMODULE h_software) noexcept -> CD3D10Device1& { m_sp_storage->m_h_software = h_software; return *this; } auto CD3D10Device1::GetFlags() const noexcept -> UINT { return m_sp_storage->m_flags; } auto CD3D10Device1::SetFlags(UINT flags) noexcept -> CD3D10Device1& { m_sp_storage->m_flags = flags; return *this; } auto CD3D10Device1::GetFeaturelLevel() const noexcept -> D3D10_FEATURE_LEVEL1 { return m_sp_storage->m_feature_level; } auto CD3D10Device1::SetFeaturelLevel(D3D10_FEATURE_LEVEL1 featurel_level) noexcept -> CD3D10Device1& { m_sp_storage->m_feature_level = featurel_level; return *this; } auto CD3D10Device1::GetSdkVersion() const noexcept -> UINT { return m_sp_storage->m_sdk_version; } auto CD3D10Device1::SetSdkVersion(UINT sdk_version) noexcept -> CD3D10Device1& { m_sp_storage->m_sdk_version = sdk_version; return *this; } auto CD3D10Device1::GetStorage() noexcept -> std::shared_ptr { return m_sp_storage; } bool CD3D10Support1::CheckSupport() { static bool result = FunctionChecker::CheckFunctionExist(_T("D3D10_1.dll"), "D3D10CreateDevice1"); return result; } auto CD3D10Support1::GetDeviceList(bool force_refresh) -> const std::vector>& { static CNullable>> p_dxgi_factory1_wrapper{}; if (!p_dxgi_factory1_wrapper || force_refresh) { Destroy(&p_dxgi_factory1_wrapper); EmplaceAt(&p_dxgi_factory1_wrapper); p_dxgi_factory1_wrapper.Construct([](ComPtr* p_content) { CreateDXGIFactory1(IID_PPV_ARGS(&*p_content)); }); } using DeviceVector = std::vector>; static CNullable> result{}; if (!result || force_refresh) { Destroy(&result); EmplaceAt(&result); result.Construct([](DeviceVector* p_content) { auto p_dxgi_factory1 = p_dxgi_factory1_wrapper.GetUnsafe().Get(); UINT i = 0; do { ComPtr p_current_adapter1{}; auto hr = p_dxgi_factory1->EnumAdapters1(i, &p_current_adapter1); if (hr == S_OK) { ++i; p_content->emplace_back(std::move(p_current_adapter1)); continue; } else if (hr == DXGI_ERROR_NOT_FOUND) { break; } else if (FAILED(hr)) { ThrowIfFailed( hr, TRAFFICMONITOR_ERROR_STR("EnumAdapters1 failed. Maybe ppAdapter parameter is NULL.")); } } while (true); }); } return result.GetUnsafe().Get(); } CDXShaderException::CDXShaderException(HRESULT hr, const char* p_error, Microsoft::WRL::ComPtr p_shader_error) : CHResultException{hr, p_error}, m_error{p_error} { if (p_shader_error != nullptr) { m_error += static_cast(p_shader_error->GetBufferPointer()); } } const char* CDXShaderException::what() const noexcept { return m_error.c_str(); } auto CShader::Compile() const -> Microsoft::WRL::ComPtr { if (m_is_macro_changed && !m_macros.empty()) { m_shader_macros.resize(m_macros.size() + 1); std::transform(m_macros.begin(), m_macros.end(), m_shader_macros.begin(), [](const ShaderMacro& current) -> D3D_SHADER_MACRO { return {current.m_name.c_str(), current.m_definition.c_str()}; }); m_shader_macros.back() = {NULL, NULL}; } m_is_macro_changed = false; if (m_is_config_changed) { if (!CDllFunctions::D3DCompile.HasValue()) { throw std::runtime_error{ TRAFFICMONITOR_ERROR_STR("Can not find function D3DCompile in d3dcompiler_47.dll or d3dcompiler_47.dll is not exist.")}; } ComPtr p_error_message{}; ThrowIfFailed( CDllFunctions::D3DCompile( m_code.c_str(), m_code.size(), m_name.c_str(), m_macros.empty() ? NULL : m_shader_macros.data(), m_p_include == nullptr ? D3D_COMPILE_STANDARD_FILE_INCLUDE : m_p_include, m_entry_point.c_str(), m_target.c_str(), m_flags1, m_flags2, &m_p_cached_byte_code, &p_error_message), TRAFFICMONITOR_ERROR_STR("Compile DX shader failed."), p_error_message); m_is_config_changed = false; } return m_p_cached_byte_code; } auto CShader::GetCode() const noexcept -> const std::string& { return m_code; } auto CShader::SetCode(const std::string& code) -> CShader& { m_is_config_changed = true; m_code = code; return *this; } auto CShader::GetEntryPoint() const noexcept -> const std::string& { return m_entry_point; } auto CShader::SetEntryPoint(const std::string& entry_point) -> CShader& { m_is_config_changed = true; m_entry_point = entry_point; return *this; } auto CShader::GetName() const noexcept -> const std::string& { return m_name; } auto CShader::SetName(const std::string& name) -> CShader& { m_is_config_changed = true; m_name = name; return *this; } auto CShader::GetTarget() const noexcept -> const std::string& { return m_target; } auto CShader::SetTarget(const std::string& target) -> CShader& { m_is_config_changed = true; m_target = target; return *this; } auto CShader::AddMacro(const ShaderMacro& macro) -> CShader& { m_is_config_changed = true; m_macros.push_back(macro); return *this; } auto CShader::DeleteMacro(const std::string& name) -> CShader& { m_is_config_changed = true; std::ignore = std::remove_if(m_macros.begin(), m_macros.end(), [&name](const ShaderMacro& x) { return x.m_name == name; }); return *this; } auto CShader::GetMacros() const noexcept -> const std::vector& { return m_macros; } UINT CShader::GetFlags1() const noexcept { return m_flags1; } auto CShader::SetFlags1(UINT flags1) noexcept -> CShader& { m_is_config_changed = true; m_flags1 = flags1; return *this; } UINT CShader::GetFlags2() const noexcept { return m_flags2; } auto CShader::SetFlags2(UINT flags2) noexcept -> CShader& { m_is_config_changed = true; m_flags2 = flags2; return *this; } auto CShader::GetInclude() noexcept -> ID3DInclude* { m_is_config_changed = true; return m_p_include; } auto CShader::SetInclude(ID3DInclude* p_indclude) noexcept -> CShader& { m_is_config_changed = true; m_p_include = p_indclude; return *this; } CD3D10DrawCallWaiter::CD3D10DrawCallWaiter(Microsoft::WRL::ComPtr p_device) { D3D10_QUERY_DESC query_desc{}; query_desc.Query = D3D10_QUERY_EVENT; ThrowIfFailed( p_device->CreateQuery(&query_desc, &m_p_query), TRAFFICMONITOR_ERROR_STR("Create ID3D10Query failed.")); m_p_query->End(); } HRESULT CD3D10DrawCallWaiter::Wait() const noexcept { HRESULT result{S_FALSE}; constexpr std::uint32_t MAX_QUERY_TIME = 20; for (std::uint32_t i = 0; i < MAX_QUERY_TIME; ++i) { result = m_p_query->GetData(NULL, 0, 0); if (result != S_FALSE) { return result; } } const auto start_time = std::chrono::system_clock::now(); using namespace std::chrono_literals; do { result = m_p_query->GetData(NULL, 0, 0); } while (result != S_FALSE && std::chrono::system_clock::now() - start_time < 1500ms); return result; } ================================================ FILE: TrafficMonitor/D3D10Support1.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include "HResultException.h" #include "RenderAPISupport.h" class CD3D10Exception1 final : public CHResultException { using CHResultException::CHResultException; }; class CD3D10Device1 { public: using Type = Microsoft::WRL::ComPtr; struct Data { Microsoft::WRL::ComPtr m_p_device1{}; Microsoft::WRL::ComPtr m_p_adapter1{}; D3D10_DRIVER_TYPE m_driver_type{D3D10_DRIVER_TYPE_HARDWARE}; HMODULE m_h_software{NULL}; UINT m_flags{0}; D3D10_FEATURE_LEVEL1 m_feature_level{D3D10_FEATURE_LEVEL_10_1}; UINT m_sdk_version{D3D10_1_SDK_VERSION}; }; using Resource = CDeviceResource; using Storage = MutipleStorage>>; private: std::shared_ptr m_sp_storage{std::make_shared()}; CResourceTracker m_resource_tracker{m_sp_storage}; public: void Recreate(Microsoft::WRL::ComPtr p_adapter1); auto Get() noexcept -> Microsoft::WRL::ComPtr; auto GetAdapter() noexcept -> Microsoft::WRL::ComPtr; auto GetDriverType() const noexcept -> D3D10_DRIVER_TYPE; auto SetDriverType(D3D10_DRIVER_TYPE driver_type) noexcept -> CD3D10Device1&; auto GetSoftwareHandle() const noexcept -> HMODULE; auto SetSoftwareHandle(HMODULE h_software) noexcept -> CD3D10Device1&; auto GetFlags() const noexcept -> UINT; auto SetFlags(UINT flags) noexcept -> CD3D10Device1&; auto GetFeaturelLevel() const noexcept -> D3D10_FEATURE_LEVEL1; auto SetFeaturelLevel(D3D10_FEATURE_LEVEL1 featurel_level) noexcept -> CD3D10Device1&; auto GetSdkVersion() const noexcept -> UINT; auto SetSdkVersion(UINT sdk_version) noexcept -> CD3D10Device1&; auto GetStorage() noexcept -> std::shared_ptr; }; class CD3D10Support1 { public: static bool CheckSupport(); static auto GetDeviceList(bool force_refresh = false) -> const std::vector>&; }; class CDXShaderException final : public CHResultException { private: std::string m_error; public: CDXShaderException(HRESULT hr, const char* p_error, Microsoft::WRL::ComPtr p_shader_error); ~CDXShaderException() override = default; const char* what() const noexcept override; }; struct ShaderMacro { std::string m_name{}; std::string m_definition{}; }; class CShader { private: std::string m_code{}; std::string m_entry_point{}; std::string m_name{}; std::string m_target{}; std::vector m_macros{}; mutable std::vector m_shader_macros{0}; ID3DInclude* m_p_include{}; UINT m_flags1{}; UINT m_flags2{}; mutable bool m_is_macro_changed{true}; mutable bool m_is_config_changed{true}; mutable Microsoft::WRL::ComPtr m_p_cached_byte_code{}; public: CShader() = default; ~CShader() = default; auto GetCode() const noexcept -> const std::string&; auto SetCode(const std::string& code) -> CShader&; auto GetEntryPoint() const noexcept -> const std::string&; auto SetEntryPoint(const std::string& entry_point) -> CShader&; auto GetName() const noexcept -> const std::string&; auto SetName(const std::string& name) -> CShader&; auto GetTarget() const noexcept -> const std::string&; auto SetTarget(const std::string& target) -> CShader&; auto AddMacro(const ShaderMacro& macro) -> CShader&; auto DeleteMacro(const std::string& name) -> CShader&; auto GetMacros() const noexcept -> const std::vector&; UINT GetFlags1() const noexcept; auto SetFlags1(UINT flags1) noexcept -> CShader&; UINT GetFlags2() const noexcept; auto SetFlags2(UINT flags2) noexcept -> CShader&; auto GetInclude() noexcept -> ID3DInclude*; auto SetInclude(ID3DInclude* p_indclude) noexcept -> CShader&; auto Compile() const -> Microsoft::WRL::ComPtr; }; class CD3D10DrawCallWaiter { private: Microsoft::WRL::ComPtr m_p_query{}; public: explicit CD3D10DrawCallWaiter(Microsoft::WRL::ComPtr p_device); ~CD3D10DrawCallWaiter() = default; /** * @brief * * @return HRESULT 若成功,返回S_OK,否则可能为DXGI_ERROR_DEVICE_REMOVED或DXGI_ERROR_INVALID_CALL * 若返回S_FALSE,此为函数本身的返回值,不能参考MS的文档,这代表等待超时(超过1.5s的等待时间) */ HRESULT Wait() const noexcept; }; ================================================ FILE: TrafficMonitor/DCompositionSupport.cpp ================================================ #include "stdafx.h" #include "DCompositionSupport.h" #include "Common.h" #include "DllFunctions.h" void CDCompositionDevice::Recreate(Microsoft::WRL::ComPtr p_dxgi_device) { ThrowIfFailed( CDllFunctions::DCompositionCreateDevice( p_dxgi_device.Get(), IID_PPV_ARGS(&m_p_device)), TRAFFICMONITOR_ERROR_STR("Recreate DComposition device failed.")); NotifyAllResourceWhenDeviceRecreate(m_resource_tracker, m_p_device); } auto CDCompositionDevice::GetStorage() -> std::shared_ptr { return m_resource_tracker.GetSharedResourceTrackerStorage(); } bool CDCompositionSupport::CheckSupport() { static auto result = FunctionChecker::CheckFunctionExist(_T("dcomp.dll"), "DCompositionCreateDevice"); return result; } auto CDCompositionDevice::Get() -> Microsoft::WRL::ComPtr { return m_p_device; } ================================================ FILE: TrafficMonitor/DCompositionSupport.h ================================================ #pragma once #include #include #include "HResultException.h" #include "RenderAPISupport.h" class CDCompositionException final : public CHResultException { using CHResultException::CHResultException; }; class CDCompositionDevice { public: using Resource = CDeviceResource; using Type = Microsoft::WRL::ComPtr; using Storage = storage_t>; private: Type m_p_device{}; CResourceTracker m_resource_tracker{std::make_shared()}; public: void Recreate(Microsoft::WRL::ComPtr p_dxgi_device); auto GetStorage() -> std::shared_ptr; auto Get() -> Microsoft::WRL::ComPtr; }; class CDCompositionSupport { public: static bool CheckSupport(); }; ================================================ FILE: TrafficMonitor/DisplayItem.cpp ================================================ #include "stdafx.h" #include "DisplayItem.h" #include "Common.h" #include "TrafficMonitor.h" CommonDisplayItem::CommonDisplayItem(DisplayItem item) { is_plugin = false; item_type = item; plugin_item = nullptr; } CommonDisplayItem::CommonDisplayItem(IPluginItem* item) { is_plugin = true; plugin_item = item; } bool CommonDisplayItem::operator<(const CommonDisplayItem& item) const { if (is_plugin && !item.is_plugin) return false; else if (!is_plugin && item.is_plugin) return true; else if (!is_plugin) return item_type < item.item_type; else return theApp.m_plugins.GetItemIndex(plugin_item) < theApp.m_plugins.GetItemIndex(item.plugin_item); } bool CommonDisplayItem::operator==(const CommonDisplayItem& item) const { if (is_plugin != item.is_plugin) return false; else if (!is_plugin) return item_type == item.item_type; else return plugin_item == item.plugin_item; } bool CommonDisplayItem::IsPlugin() const { return is_plugin; } DisplayItem CommonDisplayItem::ItemType() const { return item_type; } IPluginItem* CommonDisplayItem::PluginItem() const { return plugin_item; } CString CommonDisplayItem::GetItemName() const { if (is_plugin) { if (plugin_item != nullptr) return plugin_item->GetItemName(); else return CString(); } else { switch (item_type) { case TDI_UP: return CCommon::LoadText(IDS_UPLOAD); case TDI_DOWN: return CCommon::LoadText(IDS_DOWNLOAD); case TDI_TOTAL_SPEED: return CCommon::LoadText(IDS_TOTAL_NET_SPEED); case TDI_CPU: return CCommon::LoadText(IDS_CPU_USAGE); case TDI_MEMORY: return CCommon::LoadText(IDS_MEMORY_USAGE); case TDI_GPU_USAGE: return CCommon::LoadText(IDS_GPU_USAGE); case TDI_CPU_TEMP: return CCommon::LoadText(IDS_CPU_TEMPERATURE); case TDI_GPU_TEMP: return CCommon::LoadText(IDS_GPU_TEMPERATURE); case TDI_HDD_TEMP: return CCommon::LoadText(IDS_HDD_TEMPERATURE); case TDI_MAIN_BOARD_TEMP: return CCommon::LoadText(IDS_MAINBOARD_TEMPERATURE); case TDI_HDD_USAGE: return CCommon::LoadText(IDS_HDD_USAGE); case TDI_CPU_FREQ: return CCommon::LoadText(IDS_CPU_FREQ); case TDI_TODAY_TRAFFIC: return CCommon::LoadText(IDS_TRAFFIC_USED); default: ASSERT(false); break; } } return CString(); } std::wstring CommonDisplayItem::DefaultString(bool is_main_window) const { std::wstring default_text; if (is_plugin) { default_text = plugin_item->GetItemLableText(); default_text += L' '; } else { switch (item_type) { case TDI_UP: if (is_main_window) default_text = CCommon::LoadText(IDS_UPLOAD_DISP, _T(": ")); else default_text = _T("↑: "); break; case TDI_DOWN: if (is_main_window) default_text = CCommon::LoadText(IDS_DOWNLOAD_DISP, _T(": ")); else default_text = _T("↓: "); break; case TDI_TOTAL_SPEED: default_text = _T("↑↓: "); break; case TDI_TODAY_TRAFFIC: default_text = CCommon::LoadText(IDS_TRAFFIC_USED, _T(": ")); break; case TDI_CPU: default_text = _T("CPU: "); break; case TDI_CPU_TEMP: default_text = _T("CPU: "); break; case TDI_CPU_FREQ: default_text = CCommon::LoadText(IDS_CPU_FREQ, _T(": ")); break; case TDI_MEMORY: default_text = CCommon::LoadText(IDS_MEMORY_DISP, _T(": ")); break; case TDI_GPU_USAGE: default_text = CCommon::LoadText(IDS_GPU_DISP, _T(": ")); break; case TDI_GPU_TEMP: default_text = CCommon::LoadText(IDS_GPU_DISP, _T(": ")); break; case TDI_HDD_TEMP: default_text = CCommon::LoadText(IDS_HDD_DISP, _T(": ")); break; case TDI_MAIN_BOARD_TEMP: default_text = CCommon::LoadText(IDS_MAINBOARD_DISP, _T(": ")); break; case TDI_HDD_USAGE: default_text = CCommon::LoadText(IDS_HDD_DISP, _T(": ")); break; default: ASSERT(false); break; } } return default_text; } const wchar_t* CommonDisplayItem::GetItemIniKeyName() const { if (is_plugin) { return plugin_item->GetItemId(); } else { switch (item_type) { case TDI_UP: return L"up_string"; case TDI_DOWN: return L"down_string"; case TDI_CPU: return L"cpu_string"; case TDI_MEMORY: return L"memory_string"; case TDI_GPU_USAGE: return L"gpu_string"; case TDI_CPU_TEMP: return L"cpu_temp_string"; case TDI_GPU_TEMP: return L"gpu_temp_string"; case TDI_HDD_TEMP: return L"hdd_temp_string"; case TDI_MAIN_BOARD_TEMP: return L"main_board_temp_string"; case TDI_HDD_USAGE: return L"hdd_string"; case TDI_TOTAL_SPEED: return L"total_speed_string"; case TDI_CPU_FREQ: return L"cpu_freq_string"; case TDI_TODAY_TRAFFIC: return L"today_traffic_string"; } ASSERT(FALSE); return L""; } } CString CommonDisplayItem::GetItemValueText(bool is_main_window) const { if (is_plugin) { return plugin_item->GetItemValueText(); } else { const PublicSettingData* cfg_data{}; if (is_main_window) cfg_data = &theApp.m_main_wnd_data; else cfg_data = &theApp.m_taskbar_data; CString str_value; switch (item_type) { //上传、下载、总网速 case TDI_UP: case TDI_DOWN: case TDI_TOTAL_SPEED: { CString str_in_speed = CCommon::DataSizeToString(theApp.m_in_speed, *cfg_data); CString str_out_speed = CCommon::DataSizeToString(theApp.m_out_speed, *cfg_data); CString str_total_speed = CCommon::DataSizeToString(theApp.m_in_speed + theApp.m_out_speed, *cfg_data); if (!cfg_data->hide_unit || cfg_data->speed_unit == SpeedUnit::AUTO) { str_in_speed += _T("/s"); str_out_speed += _T("/s"); str_total_speed += _T("/s"); } //交换上传和下载位置 if (is_main_window && theApp.m_main_wnd_data.swap_up_down) std::swap(str_in_speed, str_out_speed); if (item_type == TDI_UP) str_value = str_out_speed; else if (item_type == TDI_DOWN) str_value = str_in_speed; else str_value = str_total_speed; } break; //CPU利用率 case TDI_CPU: str_value = CCommon::UsageToString(theApp.m_cpu_usage, *cfg_data); break; //内存利用率 case TDI_MEMORY: if (cfg_data->memory_display == MemoryDisplay::MEMORY_USED) str_value = CCommon::DataSizeToString(static_cast(theApp.m_used_memory) * 1024, cfg_data->separate_value_unit_with_space); else if (cfg_data->memory_display == MemoryDisplay::MEMORY_AVAILABLE) str_value = CCommon::DataSizeToString((static_cast(theApp.m_total_memory) - static_cast(theApp.m_used_memory)) * 1024, cfg_data->separate_value_unit_with_space); else str_value = CCommon::UsageToString(theApp.m_memory_usage, *cfg_data); break; //显卡利用率 case TDI_GPU_USAGE: str_value = CCommon::UsageToString(theApp.m_gpu_usage, *cfg_data); break; //硬盘利用率 case TDI_HDD_USAGE: str_value = CCommon::UsageToString(theApp.m_hdd_usage, *cfg_data); break; //CPU温度 case TDI_CPU_TEMP: str_value = CCommon::TemperatureToString(theApp.m_cpu_temperature, *cfg_data); break; //显卡温度 case TDI_GPU_TEMP: str_value = CCommon::TemperatureToString(theApp.m_gpu_temperature, *cfg_data); break; //硬盘温度 case TDI_HDD_TEMP: str_value = CCommon::TemperatureToString(theApp.m_hdd_temperature, *cfg_data); break; //主板温度 case TDI_MAIN_BOARD_TEMP: str_value = CCommon::TemperatureToString(theApp.m_main_board_temperature, *cfg_data); break; //CPU频率 case TDI_CPU_FREQ: str_value = CCommon::FreqToString(theApp.m_cpu_freq, *cfg_data); break; //总流量 case TDI_TODAY_TRAFFIC: str_value = CCommon::KBytesToString((theApp.m_today_up_traffic + theApp.m_today_down_traffic) / 1024u); break; default: break; } return str_value; } } CString CommonDisplayItem::GetItemValueSampleText(bool is_main_window) const { if (is_plugin) { return plugin_item->GetItemValueSampleText(); } //主窗口(用于绘制预览图) else if (is_main_window) { CString sample_str; switch (item_type) { case TDI_UP: sample_str = _T("88.8 KB/s"); break; case TDI_DOWN: sample_str = _T("88.9 KB/s"); break; case TDI_TOTAL_SPEED: sample_str = _T("90 KB/s"); break; case TDI_TODAY_TRAFFIC: sample_str = _T("100 MB"); break; case TDI_CPU: sample_str = _T("50 %"); break; case TDI_MEMORY: sample_str = _T("51 %"); break; case TDI_CPU_TEMP: case TDI_GPU_TEMP: case TDI_HDD_TEMP: case TDI_MAIN_BOARD_TEMP: sample_str = _T("40 °C"); break; case TDI_CPU_FREQ: sample_str = _T("1.0 GHz"); break; default: sample_str = _T("99"); break; } return sample_str; } //任务栏窗口(用于计算任务栏窗口宽度) else { CString sample_str; switch (item_type) { //网速 case TDI_UP: case TDI_DOWN: case TDI_TOTAL_SPEED: { wstring digits(theApp.m_taskbar_data.digits_number, L'8'); //根据数据位数生成指定个数的“8” bool hide_unit{ theApp.m_taskbar_data.hide_unit && theApp.m_taskbar_data.speed_unit != SpeedUnit::AUTO }; if (theApp.m_taskbar_data.speed_short_mode) { if (hide_unit) sample_str.Format(_T("%s."), digits.c_str()); else sample_str.Format(_T("%s.M/s"), digits.c_str()); } else { if (hide_unit) sample_str.Format(_T("%s.8"), digits.c_str()); else sample_str.Format(_T("%s.8MB/s"), digits.c_str()); } if (!hide_unit && theApp.m_taskbar_data.separate_value_unit_with_space) sample_str += _T(' '); if (theApp.m_taskbar_data.speed_short_mode && !theApp.m_taskbar_data.unit_byte && !theApp.m_taskbar_data.hide_unit) sample_str += _T('b'); } break; //占用率百分比 case TDI_CPU: case TDI_MEMORY: case TDI_GPU_USAGE: case TDI_HDD_USAGE: { sample_str = _T("100"); if (!theApp.m_taskbar_data.hide_percent) { if (theApp.m_taskbar_data.separate_value_unit_with_space) sample_str += _T(" %"); else sample_str += _T("%"); } //内存显示不为已使用百分比时 if (item_type == TDI_MEMORY) { if (theApp.m_taskbar_data.memory_display == MemoryDisplay::MEMORY_USED || theApp.m_taskbar_data.memory_display == MemoryDisplay::MEMORY_AVAILABLE) { //宽度为总内存的宽度 sample_str = CCommon::DataSizeToString(static_cast(theApp.m_total_memory) * 1024, theApp.m_taskbar_data.separate_value_unit_with_space); } } } break; //温度 case TDI_CPU_TEMP: case TDI_GPU_TEMP: case TDI_HDD_TEMP: case TDI_MAIN_BOARD_TEMP: { if (theApp.m_taskbar_data.separate_value_unit_with_space) sample_str = _T("99 °C"); else sample_str = _T("99°C"); } break; //CPU频率 case TDI_CPU_FREQ: { if (theApp.m_taskbar_data.separate_value_unit_with_space) sample_str = _T("1.00 GHz"); else sample_str = _T("1.00GHz"); } break; //流量 case TDI_TODAY_TRAFFIC: { if (theApp.m_taskbar_data.separate_value_unit_with_space) sample_str = _T("999.99 MB"); else sample_str = _T("999.99MB"); } break; } return sample_str; } } /////////////////////////////////////////////////////////////////////////////////////////////// DisplayItemSet::DisplayItemSet(std::initializer_list items) : data(items) { } void DisplayItemSet::Add(DisplayItem item) { data.insert(item); } void DisplayItemSet::Remove(DisplayItem item) { data.erase(item); } bool DisplayItemSet::Contains(DisplayItem item) const { auto iter = data.find(item); return iter != data.end(); } int DisplayItemSet::ToInt() const { int value = 0; //将set中的枚举值转换成int的每个bit位 for (const auto& item : data) { if (item <= 31) //int左移不超过31位 value |= (1 << item); } return value; } void DisplayItemSet::FromInt(int value) { data.clear(); //将int的每个bit位转换成set中的枚举值 for (const auto& item : AllDisplayItems) { if (item <= 31 && (value & (1 << item))) data.insert(item); } } bool DisplayItemSet::IsEmpty() const { return data.empty(); } ================================================ FILE: TrafficMonitor/DisplayItem.h ================================================ #pragma once #include "PluginInterface.h" //内置的显示的项目 enum DisplayItem { TDI_UP, TDI_DOWN, TDI_CPU, TDI_MEMORY, TDI_GPU_USAGE, TDI_CPU_TEMP, TDI_GPU_TEMP, TDI_HDD_TEMP, TDI_MAIN_BOARD_TEMP, TDI_HDD_USAGE, TDI_TOTAL_SPEED, TDI_CPU_FREQ, TDI_TODAY_TRAFFIC }; //所有内置显示项目的集合 const std::set AllDisplayItems { TDI_UP, TDI_DOWN, TDI_CPU, TDI_MEMORY, TDI_GPU_USAGE #ifndef WITHOUT_TEMPERATURE , TDI_CPU_TEMP, TDI_GPU_TEMP, TDI_HDD_TEMP, TDI_MAIN_BOARD_TEMP #endif , TDI_HDD_USAGE, TDI_CPU_FREQ, TDI_TOTAL_SPEED, TDI_TODAY_TRAFFIC }; //显示的项目 class CommonDisplayItem { public: CommonDisplayItem() {} CommonDisplayItem(DisplayItem item); CommonDisplayItem(IPluginItem* item); bool operator<(const CommonDisplayItem&) const; bool operator==(const CommonDisplayItem&) const; //是否为插件项目 bool IsPlugin() const; //获取内置的显示项目 DisplayItem ItemType() const; //获取插件显示项目 IPluginItem* PluginItem() const; //获取显示项目的名称 CString GetItemName() const; //获取一个显示项目的默认显示文本 std::wstring DefaultString(bool is_main_window) const; //获取一个显示项目的显示文本保存在ini文件中的key的名称 const wchar_t* GetItemIniKeyName() const; /** * @brief 获取一个显示项目的数值文本 * @param is_main_window 如果为true则为主窗口,否则为任务栏窗口 * @return 显示的文本 */ CString GetItemValueText(bool is_main_window) const; /** * @brief 获取一个显示项目的数值示例文本 * @param is_main_window 如果为true则为主窗口,否则为任务栏窗口 * @return 示例文本 */ CString GetItemValueSampleText(bool is_main_window) const; private: bool is_plugin{}; //是否为插件项目 DisplayItem item_type{}; //内置的显示项目 IPluginItem* plugin_item{}; //插件显示项目 }; class DisplayItemSet { public: DisplayItemSet(){} DisplayItemSet(std::initializer_list items); void Add(DisplayItem item); void Remove(DisplayItem item); bool Contains(DisplayItem item) const; int ToInt() const; void FromInt(int value); bool IsEmpty() const; private: std::set data; }; ================================================ FILE: TrafficMonitor/DisplayTextSettingDlg.cpp ================================================ // DisplayTextSettingDlg.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "DisplayTextSettingDlg.h" #include "TrafficMonitorDlg.h" #include "SkinManager.h" // CDisplayTextSettingDlg 对话框 IMPLEMENT_DYNAMIC(CDisplayTextSettingDlg, CBaseDialog) CDisplayTextSettingDlg::CDisplayTextSettingDlg(DispStrings& display_texts, bool main_window_text, CWnd* pParent /*=nullptr*/) : CBaseDialog(IDD_DISPLAY_TEXT_SETTINGS_DIALOG, pParent), m_display_texts(display_texts), m_main_window_text(main_window_text) { } CDisplayTextSettingDlg::~CDisplayTextSettingDlg() { } void CDisplayTextSettingDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_list_ctrl); } CString CDisplayTextSettingDlg::GetDialogName() const { return _T("DisplayTextSettingDlg"); } BEGIN_MESSAGE_MAP(CDisplayTextSettingDlg, CBaseDialog) ON_BN_CLICKED(IDC_RESTORE_DEFAULT_BUTTON, &CDisplayTextSettingDlg::OnBnClickedRestoreDefaultButton) ON_COMMAND(ID_RESTORE_DEFAULT, &CDisplayTextSettingDlg::OnRestoreDefault) ON_NOTIFY(NM_RCLICK, IDC_LIST1, &CDisplayTextSettingDlg::OnNMRClickList1) ON_WM_INITMENU() ON_NOTIFY(NM_CLICK, IDC_LIST1, &CDisplayTextSettingDlg::OnNMClickList1) END_MESSAGE_MAP() // CDisplayTextSettingDlg 消息处理程序 BOOL CDisplayTextSettingDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_ITEM), FALSE); // 设置小图标 //初始化列表控件 CRect rect; m_list_ctrl.GetClientRect(rect); m_list_ctrl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); int width0, width1; width0 = rect.Width() / 2; width1 = rect.Width() - width0 - theApp.DPI(20) - 1; m_list_ctrl.InsertColumn(0, CCommon::LoadText(IDS_ITEM), LVCFMT_LEFT, width0); //插入第0列 m_list_ctrl.InsertColumn(1, CCommon::LoadText(IDS_VALUE), LVCFMT_LEFT, width1); //插入第1列 //如果是主窗口,清除当前皮肤中没有的行 if (m_main_window_text) { std::set all_skin_items; CTrafficMonitorDlg::Instance()->GetCurSkin().GetSkinDisplayItems(all_skin_items); DispStrings temp = m_display_texts; m_display_texts = DispStrings(); for (const auto& display_item : all_skin_items) { m_display_texts.Get(display_item) = temp.GetConst(display_item); } } //向列表中插入行 for (auto iter = m_display_texts.GetAllItems().begin(); iter != m_display_texts.GetAllItems().end(); ++iter) { CString item_name = iter->first.GetItemName(); if (!item_name.IsEmpty()) { int index = m_list_ctrl.GetItemCount(); m_list_ctrl.InsertItem(index, item_name); m_list_ctrl.SetItemText(index, 1, iter->second.c_str()); m_list_ctrl.SetItemData(index, (DWORD_PTR)&(iter->first)); } } m_list_ctrl.SetEditColMethod(CListCtrlEx::EC_SPECIFIED); //设置列表可编辑 m_list_ctrl.SetEditableCol({ 1 }); //设置可编辑的列 CCommon::LoadMenuResource(m_menu, IDR_DISPLAY_ITEM_CONTEXT_MENU); //装载右键菜单 return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } CommonDisplayItem CDisplayTextSettingDlg::GetDisplayItem(int row) { CommonDisplayItem* item = (CommonDisplayItem*)m_list_ctrl.GetItemData(row); return *item; } void CDisplayTextSettingDlg::OnOK() { // TODO: 在此添加专用代码和/或调用基类 int item_count = m_list_ctrl.GetItemCount(); for (int i{}; i < item_count; i++) { CommonDisplayItem display_item = GetDisplayItem(i); m_display_texts.Get(display_item) = m_list_ctrl.GetItemText(i, 1).GetString(); } CBaseDialog::OnOK(); } void CDisplayTextSettingDlg::OnBnClickedRestoreDefaultButton() { // TODO: 在此添加控件通知处理程序代码 int item_count = m_list_ctrl.GetItemCount(); CTrafficMonitorDlg* pMainWnd = CTrafficMonitorDlg::Instance(); if (m_main_window_text && pMainWnd != nullptr) { //主窗口恢复默认显示文本时,从皮肤获取 SkinSettingData skin_setting_data; CSkinManager::SkinSettingDataFronSkin(skin_setting_data, pMainWnd->GetCurSkin()); for (int i{}; i < item_count; i++) { CommonDisplayItem display_item = GetDisplayItem(i); std::wstring default_text = skin_setting_data.disp_str.GetConst(display_item); m_list_ctrl.SetItemText(i, 1, default_text.c_str()); } } else { for (int i{}; i < item_count; i++) { CommonDisplayItem display_item = GetDisplayItem(i); std::wstring default_text = display_item.DefaultString(m_main_window_text); m_list_ctrl.SetItemText(i, 1, default_text.c_str()); } } } void CDisplayTextSettingDlg::OnRestoreDefault() { if (m_item_selected >= 0) { CTrafficMonitorDlg* pMainWnd = CTrafficMonitorDlg::Instance(); if (m_main_window_text && pMainWnd != nullptr) { //主窗口恢复默认显示文本时,从皮肤获取 SkinSettingData skin_setting_data; CSkinManager::SkinSettingDataFronSkin(skin_setting_data, pMainWnd->GetCurSkin()); CommonDisplayItem display_item = GetDisplayItem(m_item_selected); std::wstring default_text = skin_setting_data.disp_str.GetConst(display_item); m_list_ctrl.SetItemText(m_item_selected, 1, default_text.c_str()); } else { CommonDisplayItem display_item = GetDisplayItem(m_item_selected); std::wstring default_text = display_item.DefaultString(m_main_window_text); m_list_ctrl.SetItemText(m_item_selected, 1, default_text.c_str()); } } } void CDisplayTextSettingDlg::OnNMRClickList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); m_item_selected = pNMItemActivate->iItem; //弹出右键菜单 CMenu* pContextMenu = m_menu.GetSubMenu(0); //获取第一个弹出菜单 CPoint point1; //定义一个用于确定光标位置的位置 GetCursorPos(&point1); //获取当前光标的位置,以便使得菜单可以跟随光标 pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point1.x, point1.y, this); //在指定位置显示弹出菜单 *pResult = 0; } void CDisplayTextSettingDlg::OnInitMenu(CMenu* pMenu) { CBaseDialog::OnInitMenu(pMenu); bool selected_enable{ m_item_selected >= 0 }; pMenu->EnableMenuItem(ID_RESTORE_DEFAULT, MF_BYCOMMAND | (selected_enable ? MF_ENABLED : MF_GRAYED)); } void CDisplayTextSettingDlg::OnNMClickList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); m_item_selected = pNMItemActivate->iItem; *pResult = 0; } ================================================ FILE: TrafficMonitor/DisplayTextSettingDlg.h ================================================ #pragma once #include "BaseDialog.h" #include "CommonData.h" #include "ListCtrlEx.h" // CDisplayTextSettingDlg 对话框 class CDisplayTextSettingDlg : public CBaseDialog { DECLARE_DYNAMIC(CDisplayTextSettingDlg) public: CDisplayTextSettingDlg(DispStrings& display_texts, bool main_window_text = false, CWnd* pParent = nullptr); // 标准构造函数 virtual ~CDisplayTextSettingDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_DISPLAY_TEXT_SETTINGS_DIALOG }; #endif private: DispStrings& m_display_texts; CListCtrlEx m_list_ctrl; bool m_main_window_text{ false }; //如果为true,则为主窗口文本设置,否则为任务栏窗口设置 CMenu m_menu; int m_item_selected{ -1 }; protected: virtual CString GetDialogName() const override; CommonDisplayItem GetDisplayItem(int row); virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); virtual void OnOK(); afx_msg void OnBnClickedRestoreDefaultButton(); afx_msg void OnRestoreDefault(); afx_msg void OnNMRClickList1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnInitMenu(CMenu* pMenu); afx_msg void OnNMClickList1(NMHDR* pNMHDR, LRESULT* pResult); }; ================================================ FILE: TrafficMonitor/DllFunctions.cpp ================================================ #include "stdafx.h" #include "DllFunctions.h" CDllFunctions::CDllFunctions() { // shellscalingapi m_shcore_module = ::LoadLibrary(_T("Shcore.dll")); if (m_shcore_module != NULL) { m_getDpiForMonitor = (_GetDpiForMonitor)::GetProcAddress(m_shcore_module, "GetDpiForMonitor"); } } CDllFunctions::~CDllFunctions() { if (m_shcore_module != NULL) { FreeLibrary(m_shcore_module); m_shcore_module = NULL; } } HRESULT CDllFunctions::GetDpiForMonitor(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY) { if (m_getDpiForMonitor != nullptr) return m_getDpiForMonitor(hmonitor, dpiType, dpiX, dpiY); return E_NOINTERFACE; } #define TRAFFICMONITOR_DEFINE_STATIC_MEMBER_IN_DLL_FUNCTIONS(member_name, ...) \ decltype(CDllFunctions::member_name) CDllFunctions::member_name(__VA_ARGS__) TRAFFICMONITOR_DEFINE_STATIC_MEMBER_IN_DLL_FUNCTIONS( D3DCompile, _T("d3dcompiler_47.dll"), "D3DCompile"); TRAFFICMONITOR_DEFINE_STATIC_MEMBER_IN_DLL_FUNCTIONS( DCompositionCreateDevice, _T("dcomp.dll"), "DCompositionCreateDevice"); TRAFFICMONITOR_DEFINE_STATIC_MEMBER_IN_DLL_FUNCTIONS( CreateDXGIFactory2, _T("dxgi.dll"), "CreateDXGIFactory2"); ================================================ FILE: TrafficMonitor/DllFunctions.h ================================================ #pragma once #include // 包含::GetDpiForMonitor #include // 包含D3DCompile #include // 包含DCompositionCreateDevice #include // 包含CreateDXGIFactory2 #include #include template class CDllFunction #ifdef WIN32 { // workaround for MSVC 19.34.31933 target x86 private: FunctionPointer m_p_function{nullptr}; HMODULE m_h_dll; public: CDllFunction(LPCTSTR dll_name, LPCSTR function_name) noexcept { m_h_dll = ::LoadLibrary(dll_name); if (m_h_dll != NULL) { m_p_function = (FunctionPointer)::GetProcAddress(m_h_dll, function_name); if (m_p_function == nullptr) { ::FreeLibrary(m_h_dll); m_h_dll = NULL; } } } ~CDllFunction() noexcept { if (m_h_dll != NULL) { ::FreeLibrary(m_h_dll); m_h_dll = NULL; } } template auto operator()(Args&&... args) const { return m_p_function(std::forward(args)...); } bool HasValue() const noexcept { return m_p_function != nullptr; } } #endif ; /** * @brief 可以自动管理HMODULE生命周期,并自动尝试加载指定函数的类 使用示例: 定义:CDllFunction GetDpiForMonitor{_T("Shcore.dll"), "GetDpiForMonitor"}; 使用:(某个类或者命名空间)::GetDpiForMonitor(所需的参数); 当然,变量GetDpiForMonitor不能暴露在全局命名空间内。 * * @tparam R 函数返回值 * @tparam Args 函数参数 */ template class CDllFunction { using FunctionPointer = R (*)(Args...); private: FunctionPointer m_p_function{nullptr}; HMODULE m_h_dll; public: CDllFunction(LPCTSTR dll_name, LPCSTR function_name) noexcept { m_h_dll = ::LoadLibrary(dll_name); if (m_h_dll != NULL) { m_p_function = (FunctionPointer)::GetProcAddress(m_h_dll, function_name); if (m_p_function == nullptr) { ::FreeLibrary(m_h_dll); m_h_dll = NULL; } } } ~CDllFunction() noexcept { if (m_h_dll != NULL) { ::FreeLibrary(m_h_dll); m_h_dll = NULL; } } R operator()(Args... args) const { return m_p_function(args...); } bool HasValue() const noexcept { return m_p_function != nullptr; } }; typedef HRESULT(WINAPI* _GetDpiForMonitor)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); class CDllFunctions { public: CDllFunctions(); ~CDllFunctions(); public: HRESULT GetDpiForMonitor(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); static const CDllFunction D3DCompile; static const CDllFunction DCompositionCreateDevice; static const CDllFunction CreateDXGIFactory2; private: _GetDpiForMonitor m_getDpiForMonitor{}; private: HMODULE m_shcore_module{}; }; ================================================ FILE: TrafficMonitor/DrawCommon.cpp ================================================ #include "stdafx.h" #include "DrawCommon.h" #include "TrafficMonitor.h" CDrawCommon::CDrawCommon() { } CDrawCommon::~CDrawCommon() { } void CDrawCommon::Create(CDC* pDC, CWnd* pMainWnd) { m_pDC = pDC; m_pMainWnd = pMainWnd; if (pMainWnd != nullptr) m_pfont = m_pMainWnd->GetFont(); } void CDrawCommon::SetFont(CFont* pfont) { m_pfont = pfont; m_pDC->SelectObject(m_pfont); } void CDrawCommon::SetDC(CDC* pDC) { m_pDC = pDC; } void CDrawCommon::DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color, Alignment align, bool draw_back_ground, bool multi_line, BYTE alpha) { m_pDC->SetTextColor(color); if (!draw_back_ground) m_pDC->SetBkMode(TRANSPARENT); m_pDC->SelectObject(m_pfont); CSize text_size = m_pDC->GetTextExtent(lpszString); auto format = DrawCommonHelper::ProccessTextFormat(rect, text_size, align, multi_line); if (draw_back_ground) m_pDC->FillSolidRect(rect, m_back_color); m_pDC->DrawText(lpszString, rect, format); } void CDrawCommon::SetDrawRect(CRect rect) { CRgn rgn; rgn.CreateRectRgnIndirect(rect); m_pDC->SelectClipRgn(&rgn); } void CDrawCommon::SetDrawRect(CDC* pDC, CRect rect) { CRgn rgn; rgn.CreateRectRgnIndirect(rect); pDC->SelectClipRgn(&rgn); } void CDrawCommon::DrawBitmap(CBitmap& bitmap, CPoint start_point, CSize size, StretchMode stretch_mode) { CDC memDC; //获取图像实际大小 BITMAP bm; GetObject(bitmap, sizeof(BITMAP), &bm); memDC.CreateCompatibleDC(m_pDC); memDC.SelectObject(&bitmap); // 以下两行避免图片失真 m_pDC->SetStretchBltMode(HALFTONE); m_pDC->SetBrushOrg(0, 0); DrawCommonHelper::ImageDrawAreaConvert(CSize(bm.bmWidth, bm.bmHeight), start_point, size, stretch_mode); m_pDC->StretchBlt(start_point.x, start_point.y, size.cx, size.cy, &memDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); memDC.DeleteDC(); } void CDrawCommon::DrawBitmap(UINT bitmap_id, CPoint start_point, CSize size, StretchMode stretch_mode) { CBitmap bitmap; bitmap.LoadBitmap(bitmap_id); DrawBitmap(bitmap, start_point, size, stretch_mode); } void CDrawCommon::DrawBitmap(HBITMAP hbitmap, CPoint start_point, CSize size, StretchMode stretch_mode, BYTE) { CBitmap bitmap; if (!bitmap.Attach(hbitmap)) return; DrawBitmap(bitmap, start_point, size, stretch_mode); bitmap.Detach(); } void CDrawCommon::DrawIcon(HICON hIcon, CPoint start_point, CSize size) { if (m_pDC->GetSafeHdc() == NULL) return; if (size.cx == 0 || size.cy == 0) ::DrawIconEx(m_pDC->GetSafeHdc(), start_point.x, start_point.y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_DEFAULTSIZE); else ::DrawIconEx(m_pDC->GetSafeHdc(), start_point.x, start_point.y, hIcon, size.cx, size.cy, 0, NULL, DI_NORMAL); } void CDrawCommon::BitmapStretch(CImage* pImage, CImage* ResultImage, CSize size) { if (pImage->IsDIBSection()) { // 取得 pImage 的 DC CDC* pImageDC1 = CDC::FromHandle(pImage->GetDC()); // Image 因為有自己的 DC, 所以必須使用 FromHandle 取得對應的 DC CBitmap* bitmap1 = pImageDC1->GetCurrentBitmap(); BITMAP bmpInfo; bitmap1->GetBitmap(&bmpInfo); // 建立新的 CImage ResultImage->Create(size.cx, size.cy, bmpInfo.bmBitsPixel); CDC* ResultImageDC = CDC::FromHandle(ResultImage->GetDC()); // 當 Destination 比較小的時候, 會根據 Destination DC 上的 Stretch Blt mode 決定是否要保留被刪除點的資訊 ResultImageDC->SetStretchBltMode(HALFTONE); // 使用最高品質的方式 ::SetBrushOrgEx(ResultImageDC->m_hDC, 0, 0, NULL); // 調整 Brush 的起點 // 把 pImage 畫到 ResultImage 上面 StretchBlt(*ResultImageDC, 0, 0, size.cx, size.cy, *pImageDC1, 0, 0, pImage->GetWidth(), pImage->GetHeight(), SRCCOPY); // pImage->Draw(*ResultImageDC,0,0,StretchWidth,StretchHeight,0,0,pImage->GetWidth(),pImage->GetHeight()); pImage->ReleaseDC(); ResultImage->ReleaseDC(); } } void CDrawCommon::FillRect(CRect rect, COLORREF color, BYTE alpha) { m_pDC->FillSolidRect(rect, color); } void CDrawCommon::FillRectWithBackColor(CRect rect) { m_pDC->FillSolidRect(rect, m_back_color); } void CDrawCommon::DrawRectOutLine(CRect rect, COLORREF color, int width, bool dot_line, BYTE alpha) { CPen aPen, *pOldPen; aPen.CreatePen((dot_line ? PS_DOT : PS_SOLID), width, color); pOldPen = m_pDC->SelectObject(&aPen); CBrush* pOldBrush{dynamic_cast(m_pDC->SelectStockObject(NULL_BRUSH))}; rect.DeflateRect(width / 2, width / 2); m_pDC->Rectangle(rect); m_pDC->SelectObject(pOldPen); m_pDC->SelectObject(pOldBrush); // Restore the old brush aPen.DeleteObject(); } void CDrawCommon::GetRegionFromImage(CRgn& rgn, CBitmap& cBitmap, int threshold) { CDC memDC; memDC.CreateCompatibleDC(NULL); CBitmap* pOldMemBmp = NULL; pOldMemBmp = memDC.SelectObject(&cBitmap); //创建总的窗体区域,初始region为0 rgn.CreateRectRgn(0, 0, 0, 0); BITMAP bit; cBitmap.GetBitmap(&bit); //取得位图参数,这里要用到位图的长和宽 int y; for (y = 0; y < bit.bmHeight; y++) { CRgn rgnTemp; //保存临时region int iX = 0; do { //跳过透明色找到下一个非透明色的点. while (iX < bit.bmWidth && GetColorBritness(memDC.GetPixel(iX, y)) <= threshold) iX++; int iLeftX = iX; //记住这个起始点 //寻找下个透明色的点 while (iX < bit.bmWidth && GetColorBritness(memDC.GetPixel(iX, y)) > threshold) ++iX; //创建一个包含起点与重点间高为1像素的临时“region” rgnTemp.CreateRectRgn(iLeftX, y, iX, y + 1); rgn.CombineRgn(&rgn, &rgnTemp, RGN_OR); //删除临时"region",否则下次创建时和出错 rgnTemp.DeleteObject(); } while (iX < bit.bmWidth); } memDC.DeleteDC(); } int CDrawCommon::GetColorBritness(COLORREF color) { return (GetRValue(color) + GetGValue(color) + GetBValue(color)) / 3; } void CDrawCommon::DrawLine(CPoint start_point, int height, COLORREF color, BYTE alpha) { CPen aPen, *pOldPen; aPen.CreatePen(PS_SOLID, 1, color); pOldPen = m_pDC->SelectObject(&aPen); CBrush* pOldBrush{dynamic_cast(m_pDC->SelectStockObject(NULL_BRUSH))}; m_pDC->MoveTo(start_point); //移动到起始点,默认是从下向上画 m_pDC->LineTo(CPoint(start_point.x, start_point.y - height)); m_pDC->SelectObject(pOldPen); m_pDC->SelectObject(pOldBrush); // Restore the old brush aPen.DeleteObject(); } int CDrawCommon::GetTextWidth(LPCTSTR lpszString) { return m_pDC->GetTextExtent(lpszString).cx; } UINT DrawCommonHelper::ProccessTextFormat(CRect rect, CSize text_length, IDrawCommon::Alignment align, bool multi_line) noexcept { UINT result; // CDC::DrawText()函数的文本格式 if (multi_line) result = DT_EDITCONTROL | DT_WORDBREAK | DT_NOPREFIX; else result = DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; if (text_length.cx > rect.Width()) //如果文本宽度超过了矩形区域的宽度,设置了居中时左对齐 { if (align == IDrawCommon::Alignment::RIGHT) result |= DT_RIGHT; } else { switch (align) { case IDrawCommon::Alignment::RIGHT: result |= DT_RIGHT; break; case IDrawCommon::Alignment::CENTER: result |= DT_CENTER; break; } } return result; } void DrawCommonHelper::ImageDrawAreaConvert(CSize image_size, CPoint& start_point, CSize& size, IDrawCommon::StretchMode stretch_mode) { if (size.cx == 0 || size.cy == 0) //如果指定的size为0,则使用位图的实际大小绘制 { size = CSize(image_size.cx, image_size.cy); } else { if (stretch_mode == IDrawCommon::StretchMode::FILL) { float w_h_ratio, w_h_ratio_draw; //图像的宽高比、绘制大小的宽高比 w_h_ratio = static_cast(image_size.cx) / image_size.cy; w_h_ratio_draw = static_cast(size.cx) / size.cy; if (w_h_ratio > w_h_ratio_draw) //如果图像的宽高比大于绘制区域的宽高比,则需要裁剪两边的图像 { int image_width; //按比例缩放后的宽度 image_width = image_size.cx * size.cy / image_size.cy; start_point.x -= ((image_width - size.cx) / 2); size.cx = image_width; } else { int image_height; //按比例缩放后的高度 image_height = image_size.cy * size.cx / image_size.cx; start_point.y -= ((image_height - size.cy) / 2); size.cy = image_height; } } else if (stretch_mode == IDrawCommon::StretchMode::FIT) { CSize draw_size = image_size; float w_h_ratio, w_h_ratio_draw; //图像的宽高比、绘制大小的宽高比 w_h_ratio = static_cast(image_size.cx) / image_size.cy; w_h_ratio_draw = static_cast(size.cx) / size.cy; if (w_h_ratio > w_h_ratio_draw) //如果图像的宽高比大于绘制区域的宽高比 { draw_size.cy = draw_size.cy * size.cx / draw_size.cx; draw_size.cx = size.cx; start_point.y += ((size.cy - draw_size.cy) / 2); } else { draw_size.cx = draw_size.cx * size.cy / draw_size.cy; draw_size.cy = size.cy; start_point.x += ((size.cx - draw_size.cx) / 2); } size = draw_size; } } } void DrawCommonHelper::GetBitmapAlphaPixel(HBITMAP hBitmap, std::set& points) { points.clear(); BITMAP bm; GetObject(hBitmap, sizeof(BITMAP), &bm); int width = bm.bmWidth; int height = bm.bmHeight; // 获取位图的像素数据 BITMAPINFO bmpInfo = { 0 }; bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = width; bmpInfo.bmiHeader.biHeight = -height; // top-down DIB bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 32; bmpInfo.bmiHeader.biCompression = BI_RGB; HDC hdc = CreateCompatibleDC(NULL); SelectObject(hdc, hBitmap); // 分配内存存储位图像素 RGBQUAD* pPixels = new RGBQUAD[width * height]; GetDIBits(hdc, hBitmap, 0, height, pPixels, &bmpInfo, DIB_RGB_COLORS); // 遍历所有像素点 for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int index = y * width + x; //添加alpha值为0的像素点 if (pPixels[index].rgbReserved == 0) points.insert(Point(x, y)); } } delete[] pPixels; DeleteDC(hdc); } void DrawCommonHelper::FixBitmapTextAlpha(HBITMAP hBitmap, BYTE alpha, std::set alpha_points) { BITMAP bm; GetObject(hBitmap, sizeof(BITMAP), &bm); int width = bm.bmWidth; int height = bm.bmHeight; // 获取位图的像素数据 BITMAPINFO bmpInfo = { 0 }; bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = width; bmpInfo.bmiHeader.biHeight = -height; // top-down DIB bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 32; bmpInfo.bmiHeader.biCompression = BI_RGB; HDC hdc = CreateCompatibleDC(NULL); SelectObject(hdc, hBitmap); // 分配内存存储位图像素 RGBQUAD* pPixels = new RGBQUAD[width * height]; GetDIBits(hdc, hBitmap, 0, height, pPixels, &bmpInfo, DIB_RGB_COLORS); // 遍历所有像素 for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int index = y * width + x; //如果检测到alpha值为0,但是却不在alpha_points里,将其修正为正确的alpha值 if (pPixels[index].rgbReserved == 0 && !alpha_points.contains(Point(x, y))) pPixels[index].rgbReserved = alpha; // 设置Alpha通道 } } // 将修改后的像素数据写回位图 SetDIBits(hdc, hBitmap, 0, height, pPixels, &bmpInfo, DIB_RGB_COLORS); delete[] pPixels; DeleteDC(hdc); } ================================================ FILE: TrafficMonitor/DrawCommon.h ================================================ //封装的绘图类 #pragma once #include "IDrawCommon.h" #include "D2D1Support.h" #include "CommonData.h" #include "Nullable.hpp" class CDrawCommon final : public IDrawCommon { public: CDrawCommon(); ~CDrawCommon(); void Create(CDC* pDC, CWnd* pMainWnd); void SetFont(CFont* pfont) override; //设置绘制文本的字体 void SetDC(CDC* pDC); //设置绘图的DC virtual CDC* GetDC() override { return m_pDC; } void SetBackColor(COLORREF back_color, BYTE alpha = 255) override { m_back_color = back_color; } void SetTextColor(const COLORREF text_color, BYTE alpha = 255) override { m_pDC->SetTextColor(text_color); } void DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color, Alignment align = Alignment::LEFT, bool draw_back_ground = false, bool multi_line = false, BYTE alpha = 255) override; //在指定的矩形区域内绘制文本 void SetDrawRect(CRect rect) override; //设置绘图剪辑区域 static void SetDrawRect(CDC* pDC, CRect rect); //绘制一个位图 //(注意:当stretch_mode设置为StretchMode::FILL(填充)时,会设置绘图剪辑区域,如果之后需要绘制其他图形, //需要重新设置绘图剪辑区域,否则图片外的区域会无法绘制) void DrawBitmap(CBitmap& bitmap, CPoint start_point, CSize size, StretchMode stretch_mode = StretchMode::STRETCH); void DrawBitmap(UINT bitmap_id, CPoint start_point, CSize size, StretchMode stretch_mode = StretchMode::STRETCH); void DrawBitmap(HBITMAP hbitmap, CPoint start_point, CSize size, StretchMode stretch_mode = StretchMode::STRETCH, BYTE alpha = 255) override; void DrawIcon(HICON hIcon, CPoint start_point, CSize size); //将图片拉伸到指定尺寸(https://blog.csdn.net/sichuanpb/article/details/22986877) static void BitmapStretch(CImage* pImage, CImage* ResultImage, CSize size); void FillRect(CRect rect, COLORREF color, BYTE alpha = 255) override; //用纯色填充矩形 void FillRectWithBackColor(CRect rect); //使用背景色填充矩形 void DrawRectOutLine(CRect rect, COLORREF color, int width = 1, bool dot_line = false, BYTE alpha = 255) override; //绘制矩形边框。如果dot_line为true,则为虚线 //从图像创建区域,如果像素点的亮度小于threshold(取值为0~255,0为黑色,255为白色),则该像素点在区域外 //https://blog.csdn.net/tajon1226/article/details/6589180 static void GetRegionFromImage(CRgn& rgn, CBitmap& cBitmap, int threshold); void DrawLine(CPoint start_point, int height, COLORREF color, BYTE alpha = 255) override; //使用当前画笔画线 virtual int GetTextWidth(LPCTSTR lpszString) override; private: CDC* m_pDC{}; //用于绘图的CDC类的指针 CWnd* m_pMainWnd{}; //绘图窗口的句柄 CFont* m_pfont{}; COLORREF m_back_color{}; static int GetColorBritness(COLORREF color); }; //用于双缓冲绘图的类 class CDrawDoubleBuffer final : public IDrawBuffer { public: CDrawDoubleBuffer(CDC* pDC, CRect rect) : m_pDC(pDC), m_rect(rect) { if (m_pDC != nullptr) { m_memDC.CreateCompatibleDC(NULL); m_memBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); m_pOldBit = m_memDC.SelectObject(&m_memBitmap); } } ~CDrawDoubleBuffer() { if (m_pDC != nullptr) { m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(), &m_memDC, 0, 0, SRCCOPY); m_memDC.SelectObject(m_pOldBit); m_memBitmap.DeleteObject(); m_memDC.DeleteDC(); } } CDC* GetMemDC() { return &m_memDC; } private: CDC* m_pDC; CDC m_memDC; CBitmap m_memBitmap; CBitmap* m_pOldBit; CRect m_rect; }; namespace DrawCommonHelper { UINT ProccessTextFormat(CRect rect, CSize text_length, IDrawCommon::Alignment align, bool multi_line) noexcept; //根据图片拉伸模式,计算绘制图片的实际位置 //image_size[int]:图片的原始大小 //start_point[int][out]:绘制区域的起始位置 //size[int][out]:绘制区域的大小 //stretch_mode[int]:拉伸模式 void ImageDrawAreaConvert(CSize image_size, CPoint& start_point, CSize& size, IDrawCommon::StretchMode stretch_mode); struct Point { public: Point() {} Point(int x, int y) : m_x(x), m_y(y) {} bool operator==(const Point& a) const { return m_x == a.m_x && m_y == a.m_y; } bool operator<(const Point& a) const { if (m_x == a.m_x) return m_y < a.m_y; else return m_x < a.m_x; } private: int m_x{}; int m_y{}; }; //获取一个位置中完全透明的点,并保存到points中 void GetBitmapAlphaPixel(HBITMAP hBitmap, std::set& points); //修正位图中文本部分的Alpha通道 //使用了UpdateLayeredWindow后,使用GDI绘制的文本也会变得透明,此函数会遍历bitmap中alpha值为0,但是不在alpha_points中的像素,将其修正为正确的alpha值 void FixBitmapTextAlpha(HBITMAP hBitmap, BYTE alpha, std::set alpha_points); }; ================================================ FILE: TrafficMonitor/DrawCommonEx.cpp ================================================ #include "stdafx.h" #include "DrawCommonEx.h" #include "DrawCommon.h" CDrawCommonEx::CDrawCommonEx(CDC* pDC) { Create(pDC); } CDrawCommonEx::CDrawCommonEx() { } CDrawCommonEx::~CDrawCommonEx() { SAFE_DELETE(m_pGraphics); } void CDrawCommonEx::Create(CDC* pDC) { ASSERT(pDC != nullptr); m_pDC = pDC; SAFE_DELETE(m_pGraphics); m_pGraphics = new Gdiplus::Graphics(pDC->GetSafeHdc()); } void CDrawCommonEx::SetFont(CFont * pFont) { //õCDCͼʱCDCGDI+ m_pDC->SelectObject(pFont); } void CDrawCommonEx::DrawImage(Gdiplus::Image* pImage, CPoint start_point, CSize size, StretchMode stretch_mode) { m_pGraphics->SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQuality); DrawCommonHelper::ImageDrawAreaConvert(CSize(pImage->GetWidth(), pImage->GetHeight()), start_point, size, stretch_mode); m_pGraphics->DrawImage(pImage, INT(start_point.x), INT(start_point.y), INT(size.cx), INT(size.cy)); } void CDrawCommonEx::SetBackColor(COLORREF back_color, BYTE alpha) { m_back_color = CGdiPlusHelper::COLORREFToGdiplusColor(back_color, alpha); } void CDrawCommonEx::DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color, Alignment align, bool draw_back_ground, bool multi_line, BYTE alpha) { // Gdiplus::RectF rect_gdiplus = CGdiPlusHelper::CRectToGdiplusRect(rect); //Ʊ if (draw_back_ground) { Gdiplus::SolidBrush brush(m_back_color); m_pGraphics->FillRectangle(&brush, rect_gdiplus); } // Gdiplus::Font font(m_pDC->GetSafeHdc()); //ıɫ Gdiplus::SolidBrush brush(CGdiPlusHelper::COLORREFToGdiplusColor(color, alpha)); //ö뷽ʽ Gdiplus::StringFormat format; Gdiplus::StringAlignment alignment = Gdiplus::StringAlignmentNear; if (align == Alignment::CENTER) alignment = Gdiplus::StringAlignmentCenter; else if (align == Alignment::RIGHT) alignment = Gdiplus::StringAlignmentFar; format.SetAlignment(alignment); //ˮƽ뷽ʽ format.SetLineAlignment(Gdiplus::StringAlignmentCenter); //ֱ뷽ʽ UINT flags = Gdiplus::StringFormatFlagsNoFitBlackBox; if (!multi_line) flags |= Gdiplus::StringFormatFlagsNoWrap; //Զ format.SetTrimming(Gdiplus::StringTrimmingNone); //ֹıض format.SetFormatFlags(flags); //ı m_pGraphics->DrawString(lpszString, -1, &font, rect_gdiplus, &format, &brush); } void CDrawCommonEx::SetDrawRect(CRect rect) { m_pGraphics->SetClip(CGdiPlusHelper::CRectToGdiplusRect(rect)); } void CDrawCommonEx::FillRect(CRect rect, COLORREF color, BYTE alpha) { Gdiplus::RectF rect_gdiplus = CGdiPlusHelper::CRectToGdiplusRect(rect); Gdiplus::SolidBrush brush(CGdiPlusHelper::COLORREFToGdiplusColor(color, alpha)); m_pGraphics->FillRectangle(&brush, rect_gdiplus); } void CDrawCommonEx::DrawRectOutLine(CRect rect, COLORREF color, int width, bool dot_line, BYTE alpha) { } void CDrawCommonEx::DrawLine(CPoint start_point, int height, COLORREF color, BYTE alpha) { } void CDrawCommonEx::SetTextColor(const COLORREF color, BYTE alpha) { m_text_color = CGdiPlusHelper::COLORREFToGdiplusColor(color, alpha); } CDC* CDrawCommonEx::GetDC() { return m_pDC; } int CDrawCommonEx::GetTextWidth(LPCTSTR lpszString) { Gdiplus::Font font(m_pDC->GetSafeHdc()); Gdiplus::RectF textSize; m_pGraphics->MeasureString(lpszString, -1, &font, Gdiplus::PointF(0, 0), &textSize); return textSize.Width; } void CDrawCommonEx::DrawBitmap(HBITMAP hbitmap, CPoint start_point, CSize size, StretchMode stretch_mode, BYTE alpha) { } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// Gdiplus::Color CGdiPlusHelper::COLORREFToGdiplusColor(COLORREF color, BYTE alpha /*= 255*/) { return Gdiplus::Color(alpha, GetRValue(color), GetGValue(color), GetBValue(color)); } COLORREF CGdiPlusHelper::GdiplusColorToCOLORREF(Gdiplus::Color color) { return RGB(color.GetR(), color.GetG(), color.GetB()); } CRect CGdiPlusHelper::GdiplusRectToCRect(Gdiplus::RectF rect) { return CRect(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom()); } Gdiplus::RectF CGdiPlusHelper::CRectToGdiplusRect(CRect rect) { return Gdiplus::RectF(rect.left, rect.top, rect.Width(), rect.Height()); } ================================================ FILE: TrafficMonitor/DrawCommonEx.h ================================================ #pragma once #include "IDrawCommon.h" #include //ʹGDI+Ļͼ class CDrawCommonEx : public IDrawCommon { public: CDrawCommonEx(CDC* pDC); CDrawCommonEx(); ~CDrawCommonEx(); void Create(CDC* pDC); void SetFont(CFont* pFont); Gdiplus::Graphics* GetGraphics() { return m_pGraphics; } //һGDI+ͼ void DrawImage(Gdiplus::Image* pImage, CPoint start_point, CSize size, StretchMode stretch_mode); // ͨ IDrawCommon ̳ void SetBackColor(COLORREF back_color, BYTE alpha) override; void DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color, Alignment align, bool draw_back_ground, bool multi_line, BYTE alpha) override; void SetDrawRect(CRect rect) override; void FillRect(CRect rect, COLORREF color, BYTE alpha) override; void DrawRectOutLine(CRect rect, COLORREF color, int width, bool dot_line, BYTE alpha) override; void DrawLine(CPoint start_point, int height, COLORREF color, BYTE alpha) override; void SetTextColor(const COLORREF color, BYTE alpha) override; void DrawBitmap(HBITMAP hbitmap, CPoint start_point, CSize size, StretchMode stretch_mode, BYTE alpha) override; virtual CDC* GetDC() override; virtual int GetTextWidth(LPCTSTR lpszString) override; private: CDC* m_pDC{}; Gdiplus::Graphics* m_pGraphics{}; Gdiplus::Color m_text_color{}; Gdiplus::Color m_back_color{}; }; class CGdiPlusHelper { public: static Gdiplus::Color COLORREFToGdiplusColor(COLORREF color, BYTE alpha = 255); static COLORREF GdiplusColorToCOLORREF(Gdiplus::Color color); static CRect GdiplusRectToCRect(Gdiplus::RectF rect); static Gdiplus::RectF CRectToGdiplusRect(CRect rect); }; ================================================ FILE: TrafficMonitor/DrawCommonFactory.cpp ================================================ #include "stdafx.h" #include "DrawCommonFactory.h" auto GetInterfaceFromAllInvolvedDrawCommonObjects( AllInvolvedDrawCommonObjectsStorage& ref_object_storage, DrawCommonHelper::RenderType render_type, std::initializer_list initializer_list) -> std::tuple { auto& ref_draw_buffer_and_draw_common_objects = *static_cast( static_cast(std::addressof(ref_object_storage))); auto p_draw_buffer = static_cast( static_cast( std::addressof(ref_draw_buffer_and_draw_common_objects.m_draw_buffer_union_storage))); auto p_draw_common = static_cast( static_cast( std::addressof(ref_draw_buffer_and_draw_common_objects.m_draw_common_union_storage))); for (auto& initializer : initializer_list) { if (initializer.first == render_type) { initializer.second(p_draw_buffer, p_draw_common); UniqueIDrawBuffer up_draw_buffer{p_draw_buffer}; ref_object_storage.m_unique_draw_buffer.swap(up_draw_buffer); UniqueIDrawCommon up_draw_common{p_draw_common}; ref_object_storage.m_unique_draw_common.swap(up_draw_common); return {p_draw_buffer, p_draw_common}; } } throw std::runtime_error{TRAFFICMONITOR_ERROR_STR("No matching render type for initializer")}; } ================================================ FILE: TrafficMonitor/DrawCommonFactory.h ================================================ #pragma once #include "stdafx.h" #include #include "IDrawCommon.h" #include "DrawCommon.h" #include "TaskBarDlgDrawCommon.h" template struct StackObjectDeleter { using pointer = std::add_pointer_t; void operator()(pointer p_stack_object) const noexcept { if (p_stack_object) { Destroy(p_stack_object); } } }; using UniqueIDrawCommon = std::unique_ptr>; using UniqueIDrawBuffer = std::unique_ptr>; /** * @brief 新增渲染器类型后,将新类型写到下面类型的模板参数中 * */ using DrawCommonUnionStorage = AlignedUnionStorage; /** * @brief 新增缓冲器(即析构时提交窗口绘制内容到系统的对象)类型后,将新类型写入到下面类型的模板参数中 * */ using DrawBufferUnionStorage = AlignedUnionStorage; struct InvolvedDrawCommonStorages { DrawBufferUnionStorage m_draw_buffer_union_storage{}; DrawCommonUnionStorage m_draw_common_union_storage{}; }; /** * @brief 具有DrawCommon和DrawBuffer栈内存以及它们对应独占指针的栈内存块, 注意:它会先析构DrawCommon再析构DrawBuffer * */ struct AllInvolvedDrawCommonObjectsStorage { InvolvedDrawCommonStorages m_storage{}; // 先析构DrawCommon再析构DrawBuffer UniqueIDrawBuffer m_unique_draw_buffer{}; UniqueIDrawCommon m_unique_draw_common{}; AllInvolvedDrawCommonObjectsStorage() = default; ~AllInvolvedDrawCommonObjectsStorage() = default; // 禁用复制移动 AllInvolvedDrawCommonObjectsStorage(const AllInvolvedDrawCommonObjectsStorage&) = delete; AllInvolvedDrawCommonObjectsStorage& operator=(const AllInvolvedDrawCommonObjectsStorage&) = delete; }; using AllInvolvedDrawCommonObjectsInitializer = std::function; using TaggedAllInvolvedDrawCommonObjectsInitializer = std::pair; /** * @brief 从栈内存中使用对应RenderType的函数初始化渲染器和缓冲器 * * @param ref_object_storage 栈内存对象,其中包含具有该内存独占所有权的指针 * @param render_type 渲染器类型枚举 * @param initializer_list 渲染器类型枚举和对应的初始化函数的总的集合 * @return std::tuple 不具有所有权的裸指针 */ auto GetInterfaceFromAllInvolvedDrawCommonObjects( AllInvolvedDrawCommonObjectsStorage& ref_object_storage, DrawCommonHelper::RenderType render_type, std::initializer_list initializer_list) -> std::tuple; ================================================ FILE: TrafficMonitor/DrawTextManager.cpp ================================================ #include "stdafx.h" #include "DrawTextManager.h" #include EnableWriteMemoryGuard::~EnableWriteMemoryGuard() { m_state = ::VirtualProtect(m_p_memory, m_memory_size, m_last_flag, &m_last_flag); } bool EnableWriteMemoryGuard::GetState() { return m_state; } int WINAPI User32DrawTextManager::A::CustomDrawTextA(HDC hdc, LPCSTR lpchText, int cchText, LPRECT lprc, UINT format) { if (BaseSettings::GetEnable()) { return BaseSettings::m_replaced_function(hdc, lpchText, cchText, lprc, format); } else { return (BaseSettings::GetOriginalFunction())(hdc, lpchText, cchText, lprc, format); } } auto User32DrawTextManager::A::GetFunction() noexcept -> Function { return &CustomDrawTextA; } int WINAPI User32DrawTextManager::W::CustomDrawTextW(HDC hdc, LPCWSTR lpchText, int cchText, LPRECT lprc, UINT format) { if (BaseSettings::GetEnable()) { return BaseSettings::m_replaced_function(hdc, lpchText, cchText, lprc, format); } else { return (BaseSettings::GetOriginalFunction())(hdc, lpchText, cchText, lprc, format); } } auto User32DrawTextManager::W::GetFunction() noexcept -> Function { return &CustomDrawTextW; } int WINAPI User32DrawTextManager::ExA::CustomDrawTextExA(HDC hdc, LPSTR lpchText, int cchText, LPRECT lprc, UINT format, LPDRAWTEXTPARAMS lpdtp) { if (BaseSettings::GetEnable()) { return BaseSettings::m_replaced_function(hdc, lpchText, cchText, lprc, format, lpdtp); } else { return (BaseSettings::GetOriginalFunction())(hdc, lpchText, cchText, lprc, format, lpdtp); } } auto User32DrawTextManager::ExA::GetFunction() noexcept -> Function { return &CustomDrawTextExA; } int WINAPI User32DrawTextManager::ExW::CustomDrawTextExW(HDC hdc, LPWSTR lpchText, int cchText, LPRECT lprc, UINT format, LPDRAWTEXTPARAMS lpdtp) { if (BaseSettings::GetEnable()) { return BaseSettings::m_replaced_function(hdc, lpchText, cchText, lprc, format, lpdtp); } else { return (BaseSettings::GetOriginalFunction())(hdc, lpchText, cchText, lprc, format, lpdtp); } } auto User32DrawTextManager::ExW::GetFunction() noexcept -> Function { return &CustomDrawTextExW; } ================================================ FILE: TrafficMonitor/DrawTextManager.h ================================================ #pragma once #include #include #include class EnableWriteMemoryGuard { private: void* const m_p_memory; const std::size_t m_memory_size; DWORD m_last_flag; bool m_state; public: template EnableWriteMemoryGuard(T* p_memory, std::size_t memory_size = sizeof(T)) : m_p_memory{p_memory}, m_memory_size{memory_size} { m_state = ::VirtualProtect(m_p_memory, m_memory_size, PAGE_EXECUTE_READWRITE, &m_last_flag); } ~EnableWriteMemoryGuard(); bool GetState(); }; template struct to_std_function; template struct to_std_function { using type = std::function; }; template using to_std_function_t = typename to_std_function::type; /** * @brief 用于管理hook后的User32.dll中的DrawText系列函数的行为,目前只有D2D渲染在使用 * */ class User32DrawTextManager { private: enum class DrawTextType { DrawTextA, DrawTextW, DrawTextExA, DrawTextExW }; /** * @brief 各个类的设置数据合集,为了方便内部的非成员函数访问,内部都是静态成员 * * @tparam Owner 拥有设置数据的类类型,防止多个类共享一套数据 * @tparam Function 要储存的函数指针类型 */ template {}>> struct CommonSettings { private: static bool m_enabled; protected: using ReplacedFunction = to_std_function_t; static ReplacedFunction m_replaced_function; public: static void** m_p_iat_old_function_pointer; static Function m_old_function_pointer; struct State { bool m_is_enabled; ReplacedFunction m_replaced_function; }; static void SetEnable(bool is_enabled) noexcept { m_enabled = is_enabled; } static bool GetEnable() noexcept { return m_enabled; } static void SetReplacedFunction(ReplacedFunction replaced_function) noexcept { m_replaced_function = replaced_function; } static auto GetReplacedFunction() noexcept -> ReplacedFunction { return m_replaced_function; } static void SetState(const State& state) noexcept { m_enabled = state.m_is_enabled; if (state.m_is_enabled) { m_replaced_function = state.m_replaced_function; } else { m_replaced_function = nullptr; } } static auto GetOriginalFunction() noexcept -> Function { return m_old_function_pointer; } }; template static inline void FunctionReplacer(void** p_found_function_pointer, void* p_args) noexcept { //保存原始的函数指针 decltype(T::BaseSettings::m_p_iat_old_function_pointer) p_iat_function = &*p_found_function_pointer; T::BaseSettings::m_p_iat_old_function_pointer = p_iat_function; //保存原始函数指针的地址 decltype(T::BaseSettings::m_old_function_pointer) iat_function = reinterpret_cast(*p_found_function_pointer); T::BaseSettings::m_old_function_pointer = iat_function; //替换IAT中的函数 EnableWriteMemoryGuard enable_write{p_found_function_pointer}; auto p_custom_function = T::GetFunction(); //规避msvc扩展 ::memcpy(p_found_function_pointer, &p_custom_function, sizeof(p_custom_function)); } public: constexpr static int CUSTOM_SUCCESS = 0x7777; class A : public CommonSettings { private: static int WINAPI CustomDrawTextA(HDC hdc, LPCSTR lpchText, int cchText, LPRECT lprc, UINT format); public: using BaseSettings = CommonSettings; using Function = decltype(&CustomDrawTextA); using ReplacedFunction = typename BaseSettings::ReplacedFunction; static auto GetFunction() noexcept -> Function; static auto GetReplaceOperation() { return [](void** p_found_function_pointer, void* p_args) noexcept { FunctionReplacer(p_found_function_pointer, p_args); }; } }; class W : public CommonSettings { private: static int WINAPI CustomDrawTextW(HDC hdc, LPCWSTR lpchText, int cchText, LPRECT lprc, UINT format); public: using BaseSettings = CommonSettings; using Function = decltype(&CustomDrawTextW); using ReplacedFunction = typename BaseSettings::ReplacedFunction; static auto GetFunction() noexcept -> Function; static auto GetReplaceOperation() { return [](void** p_found_function_pointer, void* p_args) noexcept { FunctionReplacer(p_found_function_pointer, p_args); }; } }; class ExA : public CommonSettings { private: static int WINAPI CustomDrawTextExA(HDC hdc, LPSTR lpchText, int cchText, LPRECT lprc, UINT format, LPDRAWTEXTPARAMS lpdtp); public: using BaseSettings = CommonSettings; using Function = decltype(&CustomDrawTextExA); using ReplacedFunction = typename BaseSettings::ReplacedFunction; static auto GetFunction() noexcept -> Function; static auto GetReplaceOperation() { return [](void** p_found_function_pointer, void* p_args) noexcept { FunctionReplacer(p_found_function_pointer, p_args); }; } }; class ExW : public CommonSettings { private: static int WINAPI CustomDrawTextExW(HDC hdc, LPWSTR lpchText, int cchText, LPRECT lprc, UINT format, LPDRAWTEXTPARAMS lpdtp); public: using BaseSettings = CommonSettings; using Function = decltype(&CustomDrawTextExW); using ReplacedFunction = typename BaseSettings::ReplacedFunction; static auto GetFunction() noexcept -> Function; static auto GetReplaceOperation() { return [](void** p_found_function_pointer, void* p_args) noexcept { FunctionReplacer(p_found_function_pointer, p_args); }; } }; User32DrawTextManager() = delete; ~User32DrawTextManager() = delete; }; template bool ::User32DrawTextManager::CommonSettings::m_enabled = false; template Function(::User32DrawTextManager::CommonSettings::m_old_function_pointer) = nullptr; template void**(::User32DrawTextManager::CommonSettings::m_p_iat_old_function_pointer) = nullptr; template to_std_function_t(::User32DrawTextManager::CommonSettings::m_replaced_function) = {}; ================================================ FILE: TrafficMonitor/Dxgi1Support2.cpp ================================================ #include "stdafx.h" #include "Dxgi1Support2.h" #include "Common.h" #include "DllFunctions.h" void CDxgiSwapChain1::Recreate(Microsoft::WRL::ComPtr p_device, const DXGI_SWAP_CHAIN_DESC1& ref_desc1, IDXGIOutput* p_output) { CallFunctionForEachResource(m_resource_tracker); ThrowIfFailed( CDxgi1Support2::GetFactory()->CreateSwapChainForComposition( p_device.Get(), &ref_desc1, p_output, &m_p_swap_chain1), TRAFFICMONITOR_ERROR_STR("Create swap chain for composition failed.")); CallFunctionForEachResource(m_resource_tracker, m_p_swap_chain1); } auto CDxgiSwapChain1::GetStorage() -> std::shared_ptr { return m_resource_tracker.GetSharedResourceTrackerStorage(); } void CDxgiSwapChain1::Resize(std::uint32_t width, std::uint32_t height) { CallFunctionForEachResource(m_resource_tracker); ThrowIfFailed( m_p_swap_chain1->ResizeBuffers( 0, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0), TRAFFICMONITOR_ERROR_STR("Resize swap chain failed.")); CallFunctionForEachResource(m_resource_tracker, m_p_swap_chain1); } bool CDxgi1Support2::CheckSupport() { const static auto result = FunctionChecker::CheckFunctionExist(_T("dxgi.dll"), "CreateDXGIFactory2"); return result; } IDXGIFactory2* CDxgi1Support2::GetFactory() { static auto result = MakeStaticVariableWrapper( [](auto pp_factory) { *pp_factory = nullptr; auto flags = 0; #ifdef DEBUG flags |= DXGI_CREATE_FACTORY_DEBUG; #endif ThrowIfFailed( CDllFunctions::CreateDXGIFactory2( flags, IID_PPV_ARGS(pp_factory)), TRAFFICMONITOR_ERROR_STR("Create dxgi factory2 failed.")); }, [](auto pp_factory) { RELEASE_COM(*pp_factory); }); return result.Get(); } ================================================ FILE: TrafficMonitor/Dxgi1Support2.h ================================================ #pragma once #include #include "HResultException.h" #include "RenderAPISupport.h" #pragma comment(lib, "DXGI.lib") class CDxgiException final : public CHResultException { using CHResultException::CHResultException; }; template class CDxgiSwapChainResource : public CDeviceResourceBase { public: using Base = CDeviceResourceBase; using Base::Base; using typename Base::DeviceType; /** * @brief 在准备Resize交换链时,此函数将被调用,用于释放所有和交换链相关的资源。\n 注意:此函数可能被多次调用,且调用次数不一定与OnSwapChainResizeEnd的次数相匹配, 因此必须保证资源可以被反复执行释放操作 * */ virtual void OnSwapChainResizeBegin() noexcept = 0; /** * @brief 在交换链完成Resize后,此函数将被调用,用于重建所有和交换链相关的资源 * */ virtual void OnSwapChainResizeEnd(DeviceType p_resized_swap_chain) noexcept = 0; }; /** * @brief 此类对应的资源为CDxgiSwapChainResource,而非CDeviceResource * */ class CDxgiSwapChain1 { public: using Resource = CDxgiSwapChainResource; using Type = Microsoft::WRL::ComPtr; using Storage = storage_t>; private: Type m_p_swap_chain1{}; CResourceTracker m_resource_tracker{std::make_shared()}; public: void Recreate(Microsoft::WRL::ComPtr p_device, const DXGI_SWAP_CHAIN_DESC1& ref_desc1, IDXGIOutput* p_output = nullptr); auto GetStorage() -> std::shared_ptr; void Resize(std::uint32_t width, std::uint32_t height); }; class CDxgi1Support2 { public: static bool CheckSupport(); static IDXGIFactory2* GetFactory(); }; ================================================ FILE: TrafficMonitor/FileDialogEx.cpp ================================================ #include "stdafx.h" #include "FileDialogEx.h" #include CFileDialogEx::CFileDialogEx(BOOL bOpenFileDialog, LPCWSTR lpszDefExt, LPCWSTR lpszFilter) : pFileDialog(nullptr), m_bOpenFileDialog(bOpenFileDialog) { // 创建 IFileDialog 实例 HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileDialog)); if (pFileDialog) { // 设置对话框选项 DWORD dwOptions; pFileDialog->GetOptions(&dwOptions); pFileDialog->SetOptions(dwOptions | FOS_FORCEFILESYSTEM); // 仅文件系统项 if (bOpenFileDialog) { pFileDialog->SetOptions(dwOptions | FOS_FILEMUSTEXIST); // 打开文件时文件必须存在 } // 设置默认扩展名 if (lpszDefExt) { pFileDialog->SetDefaultExtension(lpszDefExt); } // 解析并设置过滤器 if (lpszFilter) { ParseFilter(lpszFilter); for (size_t i = 0; i < m_filterDescriptions.size() && i < m_filterSpecsStrings.size(); i++) { COMDLG_FILTERSPEC filterSpec = { m_filterDescriptions[i].c_str(), m_filterSpecsStrings[i].c_str()}; m_filterSpecs.push_back(filterSpec); } pFileDialog->SetFileTypes(m_filterSpecs.size(), m_filterSpecs.data()); } } } CFileDialogEx::~CFileDialogEx() { if (pFileDialog) { pFileDialog->Release(); } } int CFileDialogEx::DoModal(HWND hWndOwner) { if (!pFileDialog) return -1; // 显示对话框 HRESULT hr = pFileDialog->Show(hWndOwner); if (FAILED(hr)) return -1; // 获取选定的文件路径 IShellItem* pItem = nullptr; hr = pFileDialog->GetResult(&pItem); if (SUCCEEDED(hr)) { PWSTR pszFilePath = nullptr; hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); if (SUCCEEDED(hr)) { m_filePath = pszFilePath; CoTaskMemFree(pszFilePath); } pItem->Release(); } return (SUCCEEDED(hr)) ? 1 : 0; } std::wstring CFileDialogEx::GetPathName() const { return m_filePath; } std::wstring CFileDialogEx::GetFileName() const { // 获取文件名 size_t pos = m_filePath.find_last_of(L"\\"); if (pos != std::wstring::npos) { return m_filePath.substr(pos + 1); } return m_filePath; } void CFileDialogEx::ParseFilter(LPCWSTR lpszFilter) { m_filterSpecs.clear(); m_filterDescriptions.clear(); m_filterSpecsStrings.clear(); // 使用 | 分割过滤器字符串 std::wstringstream filterStream(lpszFilter); std::wstring description, spec; while (std::getline(filterStream, description, L'|')) { if (std::getline(filterStream, spec, L'|')) { // 将描述和模式存储在 m_filterDescriptions 和 m_filterSpecsStrings 中 m_filterDescriptions.push_back(description); m_filterSpecsStrings.push_back(spec); } } } ================================================ FILE: TrafficMonitor/FileDialogEx.h ================================================ #pragma once class CFileDialogEx { public: CFileDialogEx(BOOL bOpenFileDialog, LPCWSTR lpszDefExt = NULL, LPCWSTR lpszFilter = NULL); ~CFileDialogEx(); // 显示对话框 int DoModal(HWND hWndOwner = NULL); // 获取选定的文件路径和文件名 std::wstring GetPathName() const; std::wstring GetFileName() const; private: void ParseFilter(LPCWSTR lpszFilter); private: IFileDialog* pFileDialog; std::wstring m_filePath; BOOL m_bOpenFileDialog; std::vector m_filterSpecs; // 存储过滤器描述和模式 std::vector m_filterDescriptions; // 存储过滤器描述 std::vector m_filterSpecsStrings; // 存储过滤器模式 }; ================================================ FILE: TrafficMonitor/FilePathHelper.cpp ================================================ #include "stdafx.h" #include "FilePathHelper.h" #include "Common.h" CFilePathHelper::CFilePathHelper(const wstring & file_path) : m_file_path{ file_path } { } CFilePathHelper::~CFilePathHelper() { } wstring CFilePathHelper::GetFileExtension(bool upper, bool width_dot) const { size_t index; index = m_file_path.rfind('.'); if (index == wstring::npos || index == m_file_path.size() - 1) return wstring(); wstring file_extension{ m_file_path.substr(width_dot ? index : index + 1) }; CCommon::StringTransform(file_extension, upper); return file_extension; } wstring CFilePathHelper::GetFileName() const { size_t index; index = m_file_path.rfind('\\'); if (index == wstring::npos) index = m_file_path.rfind('/'); return m_file_path.substr(index + 1); } wstring CFilePathHelper::GetFileNameWithoutExtension() const { size_t index, index1; index = m_file_path.rfind('.'); index1 = m_file_path.rfind('\\'); if (index1 == wstring::npos) index1 = m_file_path.rfind('/'); return m_file_path.substr(index1 + 1, (index - index1 - 1)); } wstring CFilePathHelper::GetFolderName() const { int index, index1; index = m_file_path.find_last_of(L"\\/"); if (index == wstring::npos || index == 0) return wstring(); index1 = m_file_path.find_last_of(L"\\/", index - 1); if (index1 == wstring::npos || index1 == 0) return wstring(); return m_file_path.substr(index1 + 1, (index - index1 - 1)); } wstring CFilePathHelper::GetDir() const { if (!m_file_path.empty() && (m_file_path.back() == L'\\' || m_file_path.back() == L'/')) return m_file_path; size_t index; index = m_file_path.rfind('\\'); if (index == wstring::npos) index = m_file_path.rfind('/'); return m_file_path.substr(0, index + 1); } wstring CFilePathHelper::GetParentDir() const { wstring dir{ GetDir() }; size_t index; if (!dir.empty() && (dir.back() == L'\\' || dir.back() == L'/')) dir.pop_back(); index = dir.rfind('\\'); if (index == wstring::npos) index = dir.rfind('/'); return m_file_path.substr(0, index + 1); } const wstring& CFilePathHelper::ReplaceFileExtension(const wchar_t * new_extension) { size_t index, index1; index = m_file_path.rfind('.'); index1 = m_file_path.rfind('\\'); if (index == wstring::npos || (index1 != wstring::npos && index < index1)) //如果没有找到“.”,或者“.”在反斜杠的左边,则在末尾添加一个“.” { m_file_path.push_back(L'.'); } else if (index != m_file_path.size() - 1) //如果“.”不在最后的位置,则删除“.”后面的字符串 { m_file_path.erase(index + 1); } if (new_extension == nullptr || *new_extension == L'\0') { if (!m_file_path.empty() && m_file_path.back() == L'.') m_file_path.pop_back(); } else { m_file_path.append(new_extension); //在末尾添加扩展名 } return m_file_path; } wstring CFilePathHelper::GetFilePathWithoutExtension() const { size_t index; index = m_file_path.rfind('.'); return m_file_path.substr(0, index); } ================================================ FILE: TrafficMonitor/FilePathHelper.h ================================================ #pragma once class CFilePathHelper { public: CFilePathHelper(const wstring& file_path); CFilePathHelper(){} ~CFilePathHelper(); void SetFilePath(const wstring& file_path) { m_file_path = file_path; } wstring GetFileExtension(bool upper = false, bool width_dot = false) const; //获取文件的扩展名(upper:是否大写; width_dot:是否包含“.”) wstring GetFileName() const; //获取文件名 wstring GetFileNameWithoutExtension() const; //获取文件名(不含扩展名) wstring GetFolderName() const; //获取文件夹名 wstring GetDir() const; //获取目录 wstring GetParentDir() const; //获取上级目录 wstring GetFilePath() const { return m_file_path; } //获取完整路径 const wstring& ReplaceFileExtension(const wchar_t* new_extension); //替换文件的扩展名,返回文件完整路径 wstring GetFilePathWithoutExtension() const; //获取文件路径(不含扩展名) protected: wstring m_file_path; }; ================================================ FILE: TrafficMonitor/GeneralSettingsDlg.cpp ================================================ // GeneralSettingsDlg.cpp : implementation file // #include "stdafx.h" #include "TrafficMonitor.h" #include "TrafficMonitorDlg.h" #include "GeneralSettingsDlg.h" #include "PluginManagerDlg.h" #include "SelectConnectionsDlg.h" // CGeneralSettingsDlg dialog static const int MONITOR_SPAN_STEP = 100; IMPLEMENT_DYNAMIC(CGeneralSettingsDlg, CTabDlg) CGeneralSettingsDlg::CGeneralSettingsDlg(CWnd* pParent /*=NULL*/) : CTabDlg(IDD_GENERAL_SETTINGS_DIALOG, pParent) { } CGeneralSettingsDlg::~CGeneralSettingsDlg() { } void CGeneralSettingsDlg::CheckTaskbarDisplayItem() { //如果选项设置中关闭了某个硬件监控,则不显示对应的温度监控相关项目 if (!theApp.m_general_data.IsHardwareEnable(HI_CPU)) { theApp.m_taskbar_data.display_item.Remove(TDI_CPU_TEMP); } if (!theApp.m_general_data.IsHardwareEnable(HI_GPU)) { theApp.m_taskbar_data.display_item.Remove(TDI_GPU_TEMP); } if (!theApp.m_general_data.IsHardwareEnable(HI_HDD)) { theApp.m_taskbar_data.display_item.Remove(TDI_HDD_TEMP); } if (!theApp.m_general_data.IsHardwareEnable(HI_MBD)) theApp.m_taskbar_data.display_item.Remove(TDI_MAIN_BOARD_TEMP); } void CGeneralSettingsDlg::SetControlMouseWheelEnable(bool enable) { m_traffic_tip_combo.SetMouseWheelEnable(enable); m_language_combo.SetMouseWheelEnable(enable); m_traffic_tip_edit.SetMouseWheelEnable(enable); m_memory_tip_edit.SetMouseWheelEnable(enable); m_monitor_span_edit.SetMouseWheelEnable(enable); m_cpu_temp_tip_edit.SetMouseWheelEnable(enable); m_gpu_temp_tip_edit.SetMouseWheelEnable(enable); m_hdd_temp_tip_edit.SetMouseWheelEnable(enable); m_mbd_temp_tip_edit.SetMouseWheelEnable(enable); m_hard_disk_combo.SetMouseWheelEnable(enable); m_select_cpu_combo.SetMouseWheelEnable(enable); } void CGeneralSettingsDlg::OnSettingsApplied() { //当设置被应用时,重置xxxx_ori的值 m_monitor_time_span_ori = m_data.monitor_time_span; m_update_source_ori = m_data.update_source; } bool CGeneralSettingsDlg::InitializeControls() { RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_CHECK_NOW_BUTTON, CtrlTextInfo::W16 } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_UPDATE_SORUCE_STATIC }, { CtrlTextInfo::L3, IDC_GITHUB_RADIO }, { CtrlTextInfo::L2, IDC_GITEE_RADIO } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_RESET_AUTO_RUN_BUTTON, CtrlTextInfo::W16 } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_LANGUAGE_STATIC }, { CtrlTextInfo::L3, IDC_LANGUAGE_COMBO } }); //调整“今日使用流量已达到”这一行控件的水平位置 RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_TODAY_TRAFFIC_TIP_CHECK, CtrlTextInfo::W24 }, { CtrlTextInfo::L3, IDC_TODAY_TRAFFIC_TIP_EDIT }, { CtrlTextInfo::L2, IDC_TODAY_TRAFFIC_TIP_COMBO }, { CtrlTextInfo::L1, IDC_TODAY_TRAFFIC_BACK_STATIC} }); //调整“内存使用率已达到”、“温度已达到”这几行控件的水平位置 RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_MEMORY_USAGE_TIP_CHECK, CtrlTextInfo::W24 }, { CtrlTextInfo::L3, IDC_MEMORY_USAGE_TIP_EDIT }, { CtrlTextInfo::L2, IDC_MEMORY_USAGE_BACK_STATIC }, { CtrlTextInfo::L4, IDC_CPU_TEMP_TIP_CHECK, CtrlTextInfo::W24 }, { CtrlTextInfo::L3, IDC_CPU_TEMP_TIP_EDIT }, { CtrlTextInfo::L2, IDC_CPU_TEMP_STATIC }, { CtrlTextInfo::L4, IDC_GPU_TEMP_TIP_CHECK, CtrlTextInfo::W24 }, { CtrlTextInfo::L3, IDC_GPU_TEMP_TIP_EDIT }, { CtrlTextInfo::L2, IDC_GPU_TEMP_STATIC }, { CtrlTextInfo::L4, IDC_HDD_TEMP_TIP_CHECK, CtrlTextInfo::W24 }, { CtrlTextInfo::L3, IDC_HDD_TIP_EDIT }, { CtrlTextInfo::L2, IDC_HDD_STATIC }, { CtrlTextInfo::L4, IDC_MBD_TEMP_TIP_CHECK, CtrlTextInfo::W24 }, { CtrlTextInfo::L3, IDC_MBD_TEMP_TIP_EDIT }, { CtrlTextInfo::L2, IDC_MBD_TEMP_STATIC }, }); RepositionTextBasedControls({ { CtrlTextInfo::L1, IDC_SELECT_HDD_STATIC }, { CtrlTextInfo::C0, IDC_SELECT_HARD_DISK_COMBO }, { CtrlTextInfo::L1, IDC_SELECT_CPU_STATIC }, { CtrlTextInfo::C0, IDC_SELECT_CPU_COMBO }, }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_SELECT_CONNECTIONS_BUTTON, CtrlTextInfo::W32 } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_MONITOR_INTERVAL_STATIC }, { CtrlTextInfo::L3, IDC_MONITOR_SPAN_EDIT }, { CtrlTextInfo::L2, IDC_MILLISECONDS_STATIC }, { CtrlTextInfo::L1, IDC_RESTORE_DEFAULT_TIME_SPAN_BUTTON, CtrlTextInfo::W16 } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_PLUGIN_MANAGE_BUTTON, CtrlTextInfo::W32 } }); return true; } bool CGeneralSettingsDlg::ShowHardwareMonitorWarning() { //如果已经有硬件监控项目被勾选了,则不再弹出提示 if (m_data.hardware_monitor_item != 0) return true; if (SHMessageBoxCheck(m_hWnd, CCommon::LoadText(IDS_HARDWARE_MONITOR_WARNING), APP_NAME, MB_OKCANCEL | MB_ICONWARNING, IDOK, _T("{B8A281A7-76DF-4F0F-BF6A-1A394EF8BAD5}")) == IDOK) return true; return false; } void CGeneralSettingsDlg::AddOrUpdateAutoRunTooltip(bool add) { CString str_tool_tip; #ifdef WITHOUT_TEMPERATURE str_tool_tip = CCommon::LoadText(IDS_AUTO_RUN_METHOD_REGESTRY); #else str_tool_tip = CCommon::LoadText(IDS_AUTO_RUN_METHOD_TASK_SCHEDULE); #endif if (!m_auto_run_path.empty()) { str_tool_tip += _T("\r\n"); str_tool_tip += CCommon::LoadText(IDS_PATH, _T(": ")); str_tool_tip += m_auto_run_path.c_str(); } if (add) m_toolTip.AddTool(GetDlgItem(IDC_AUTO_RUN_CHECK), str_tool_tip); else m_toolTip.UpdateTipText(str_tool_tip, GetDlgItem(IDC_AUTO_RUN_CHECK)); } bool CGeneralSettingsDlg::IsMonitorTimeSpanModified() const { return m_data.monitor_time_span != m_monitor_time_span_ori; } void CGeneralSettingsDlg::DoDataExchange(CDataExchange* pDX) { CTabDlg::DoDataExchange(pDX); DDX_Control(pDX, IDC_TODAY_TRAFFIC_TIP_EDIT, m_traffic_tip_edit); DDX_Control(pDX, IDC_TODAY_TRAFFIC_TIP_COMBO, m_traffic_tip_combo); DDX_Control(pDX, IDC_MEMORY_USAGE_TIP_EDIT, m_memory_tip_edit); DDX_Control(pDX, IDC_LANGUAGE_COMBO, m_language_combo); DDX_Control(pDX, IDC_MONITOR_SPAN_EDIT, m_monitor_span_edit); DDX_Control(pDX, IDC_CPU_TEMP_TIP_EDIT, m_cpu_temp_tip_edit); DDX_Control(pDX, IDC_GPU_TEMP_TIP_EDIT, m_gpu_temp_tip_edit); DDX_Control(pDX, IDC_HDD_TIP_EDIT, m_hdd_temp_tip_edit); DDX_Control(pDX, IDC_MBD_TEMP_TIP_EDIT, m_mbd_temp_tip_edit); DDX_Control(pDX, IDC_SELECT_HARD_DISK_COMBO, m_hard_disk_combo); DDX_Control(pDX, IDC_SELECT_CPU_COMBO, m_select_cpu_combo); DDX_Control(pDX, IDC_PLUGIN_MANAGE_BUTTON, m_plugin_manager_btn); DDX_Control(pDX, IDC_SELECT_CONNECTIONS_BUTTON, m_select_connection_btn); } void CGeneralSettingsDlg::SetControlEnable() { m_traffic_tip_edit.EnableWindow(m_data.traffic_tip_enable); m_traffic_tip_combo.EnableWindow(m_data.traffic_tip_enable); m_memory_tip_edit.EnableWindow(m_data.memory_usage_tip.enable); m_cpu_temp_tip_edit.EnableWindow(m_data.cpu_temp_tip.enable); m_gpu_temp_tip_edit.EnableWindow(m_data.gpu_temp_tip.enable); m_hdd_temp_tip_edit.EnableWindow(m_data.hdd_temp_tip.enable); m_mbd_temp_tip_edit.EnableWindow(m_data.mainboard_temp_tip.enable); //m_hard_disk_combo.EnableWindow(m_data.IsHardwareEnable(HI_HDD)); m_select_cpu_combo.EnableWindow(m_data.IsHardwareEnable(HI_CPU)); EnableDlgCtrl(IDC_SELECT_CONNECTIONS_BUTTON, !m_data.show_all_interface); } BEGIN_MESSAGE_MAP(CGeneralSettingsDlg, CTabDlg) ON_BN_CLICKED(IDC_CHECK_NOW_BUTTON, &CGeneralSettingsDlg::OnBnClickedCheckNowButton) ON_BN_CLICKED(IDC_CHECK_UPDATE_CHECK, &CGeneralSettingsDlg::OnBnClickedCheckUpdateCheck) ON_BN_CLICKED(IDC_AUTO_RUN_CHECK, &CGeneralSettingsDlg::OnBnClickedAutoRunCheck) ON_BN_CLICKED(IDC_TODAY_TRAFFIC_TIP_CHECK, &CGeneralSettingsDlg::OnBnClickedTodayTrafficTipCheck) ON_BN_CLICKED(IDC_MEMORY_USAGE_TIP_CHECK, &CGeneralSettingsDlg::OnBnClickedMemoryUsageTipCheck) ON_BN_CLICKED(IDC_OPEN_CONFIG_PATH_BUTTON, &CGeneralSettingsDlg::OnBnClickedOpenConfigPathButton) ON_BN_CLICKED(IDC_SHOW_ALL_CONNECTION_CHECK, &CGeneralSettingsDlg::OnBnClickedShowAllConnectionCheck) ON_BN_CLICKED(IDC_USE_CPU_TIME_RADIO, &CGeneralSettingsDlg::OnBnClickedUseCpuTimeRadio) ON_BN_CLICKED(IDC_USE_PDH_RADIO, &CGeneralSettingsDlg::OnBnClickedUsePdhRadio) ON_EN_KILLFOCUS(IDC_MONITOR_SPAN_EDIT, &CGeneralSettingsDlg::OnEnKillfocusMonitorSpanEdit) ON_BN_CLICKED(IDC_CPU_TEMP_TIP_CHECK, &CGeneralSettingsDlg::OnBnClickedCpuTempTipCheck) ON_BN_CLICKED(IDC_GPU_TEMP_TIP_CHECK, &CGeneralSettingsDlg::OnBnClickedGpuTempTipCheck) ON_BN_CLICKED(IDC_HDD_TEMP_TIP_CHECK, &CGeneralSettingsDlg::OnBnClickedHddTempTipCheck) ON_BN_CLICKED(IDC_MBD_TEMP_TIP_CHECK, &CGeneralSettingsDlg::OnBnClickedMbdTempTipCheck) ON_BN_CLICKED(IDC_GITHUB_RADIO, &CGeneralSettingsDlg::OnBnClickedGithubRadio) ON_BN_CLICKED(IDC_GITEE_RADIO, &CGeneralSettingsDlg::OnBnClickedGiteeRadio) ON_BN_CLICKED(IDC_RESTORE_DEFAULT_TIME_SPAN_BUTTON, &CGeneralSettingsDlg::OnBnClickedRestoreDefaultTimeSpanButton) ON_CBN_SELCHANGE(IDC_SELECT_HARD_DISK_COMBO, &CGeneralSettingsDlg::OnCbnSelchangeSelectHardDiskCombo) ON_BN_CLICKED(IDC_CPU_CHECK, &CGeneralSettingsDlg::OnBnClickedCpuCheck) ON_BN_CLICKED(IDC_GPU_CHECK, &CGeneralSettingsDlg::OnBnClickedGpuCheck) ON_BN_CLICKED(IDC_HDD_CHECK, &CGeneralSettingsDlg::OnBnClickedHddCheck) ON_BN_CLICKED(IDC_MBD_CHECK, &CGeneralSettingsDlg::OnBnClickedMbdCheck) ON_CBN_SELCHANGE(IDC_SELECT_CPU_COMBO, &CGeneralSettingsDlg::OnCbnSelchangeSelectCpuCombo) ON_BN_CLICKED(IDC_PLUGIN_MANAGE_BUTTON, &CGeneralSettingsDlg::OnBnClickedPluginManageButton) ON_BN_CLICKED(IDC_SHOW_NOTIFY_ICON_CHECK, &CGeneralSettingsDlg::OnBnClickedShowNotifyIconCheck) ON_BN_CLICKED(IDC_SELECT_CONNECTIONS_BUTTON, &CGeneralSettingsDlg::OnBnClickedSelectConnectionsButton) ON_BN_CLICKED(IDC_RESET_AUTO_RUN_BUTTON, &CGeneralSettingsDlg::OnBnClickedResetAutoRunButton) ON_BN_CLICKED(IDC_USE_HARDWARE_MONITOR_RADIO, &CGeneralSettingsDlg::OnBnClickedUseHardwareMonitorRadio) ON_EN_CHANGE(IDC_MONITOR_SPAN_EDIT, &CGeneralSettingsDlg::OnEnChangeMonitorSpanEdit) ON_MESSAGE(WM_SPIN_EDIT_POS_CHANGED, &CGeneralSettingsDlg::OnSpinEditPosChanged) END_MESSAGE_MAP() // CGeneralSettingsDlg 消息处理程序 BOOL CGeneralSettingsDlg::OnInitDialog() { CTabDlg::OnInitDialog(); // TODO: 在此添加额外的初始化 ((CButton*)GetDlgItem(IDC_CHECK_UPDATE_CHECK))->SetCheck(m_data.check_update_when_start); if (theApp.IsForceShowNotifyIcon()) { m_data.show_notify_icon = true; EnableDlgCtrl(IDC_SHOW_NOTIFY_ICON_CHECK, FALSE); } CheckDlgButton(IDC_SHOW_NOTIFY_ICON_CHECK, m_data.show_notify_icon); if (m_data.update_source == 0) CheckDlgButton(IDC_GITHUB_RADIO, TRUE); else CheckDlgButton(IDC_GITEE_RADIO, TRUE); //检查开始菜单的“启动”目录下有没有程序的快捷方式,如果有则设置开机自启动,然后删除快捷方式 wstring start_up_path = CCommon::GetStartUpPath(); bool shortcut_exist = CCommon::FileExist((start_up_path + L"\\TrafficMonitor.lnk").c_str()); if (shortcut_exist) { theApp.SetAutoRun(true); m_data.auto_run = true; DeleteFile((start_up_path + L"\\TrafficMonitor.lnk").c_str()); } else { m_data.auto_run = theApp.GetAutoRun(&m_auto_run_path); } ((CButton*)GetDlgItem(IDC_SAVE_TO_APPDATA_RADIO))->SetCheck(!m_data.portable_mode); ((CButton*)GetDlgItem(IDC_SAVE_TO_PROGRAM_DIR_RADIO))->SetCheck(m_data.portable_mode); GetDlgItem(IDC_SAVE_TO_PROGRAM_DIR_RADIO)->EnableWindow(theApp.m_module_dir_writable); ((CButton*)GetDlgItem(IDC_AUTO_RUN_CHECK))->SetCheck(m_data.auto_run); ((CButton*)GetDlgItem(IDC_TODAY_TRAFFIC_TIP_CHECK))->SetCheck(m_data.traffic_tip_enable); m_traffic_tip_edit.SetRange(1, 32767); m_traffic_tip_edit.SetValue(m_data.traffic_tip_value); m_traffic_tip_combo.AddString(_T("MB")); m_traffic_tip_combo.AddString(_T("GB")); m_traffic_tip_combo.SetCurSel(m_data.traffic_tip_unit); CheckDlgButton(IDC_MEMORY_USAGE_TIP_CHECK, m_data.memory_usage_tip.enable); m_memory_tip_edit.SetRange(1, 100); m_memory_tip_edit.SetValue(m_data.memory_usage_tip.tip_value); CheckDlgButton(IDC_CPU_TEMP_TIP_CHECK, m_data.cpu_temp_tip.enable); m_cpu_temp_tip_edit.SetRange(1, 120); m_cpu_temp_tip_edit.SetValue(m_data.cpu_temp_tip.tip_value); CheckDlgButton(IDC_GPU_TEMP_TIP_CHECK, m_data.gpu_temp_tip.enable); m_gpu_temp_tip_edit.SetRange(1, 120); m_gpu_temp_tip_edit.SetValue(m_data.gpu_temp_tip.tip_value); CheckDlgButton(IDC_HDD_TEMP_TIP_CHECK, m_data.hdd_temp_tip.enable); m_hdd_temp_tip_edit.SetRange(1, 120); m_hdd_temp_tip_edit.SetValue(m_data.hdd_temp_tip.tip_value); CheckDlgButton(IDC_MBD_TEMP_TIP_CHECK, m_data.mainboard_temp_tip.enable); m_mbd_temp_tip_edit.SetRange(1, 120); m_mbd_temp_tip_edit.SetValue(m_data.mainboard_temp_tip.tip_value); SetControlEnable(); m_language_combo.AddString(CCommon::LoadText(IDS_FOLLOWING_SYSTEM)); int current_language_index{ -1 }; //当前语言在所有语言列表中的序号 for (size_t i = 0; i < theApp.m_str_table.GetLanguageList().size(); i++) { const CStrTable::LanguageInfo& language_info = theApp.m_str_table.GetLanguageList()[i]; m_language_combo.AddString(language_info.display_name.c_str()); if (language_info.language_id == m_data.language) current_language_index = static_cast(i); } m_language_combo.SetCurSel(current_language_index + 1); //由于ComboBox第一项是“跟随系统”,因此ComboBox的序号需要加1 ((CButton*)GetDlgItem(IDC_SHOW_ALL_CONNECTION_CHECK))->SetCheck(m_data.show_all_interface); m_toolTip.Create(this); m_toolTip.SetMaxTipWidth(theApp.DPI(300)); m_toolTip.AddTool(GetDlgItem(IDC_SHOW_ALL_CONNECTION_CHECK), CCommon::LoadText(IDS_SHOW_ALL_INFO_TIP)); m_toolTip.AddTool(GetDlgItem(IDC_SAVE_TO_APPDATA_RADIO), theApp.m_appdata_dir.c_str()); m_toolTip.AddTool(GetDlgItem(IDC_SAVE_TO_PROGRAM_DIR_RADIO), theApp.m_module_dir.c_str()); AddOrUpdateAutoRunTooltip(true); if (m_data.cpu_usage_acquire_method == GeneralSettingData::CA_CPU_TIME) { CheckDlgButton(IDC_USE_CPU_TIME_RADIO, TRUE); } else if (m_data.cpu_usage_acquire_method == GeneralSettingData::CA_PDH) { CheckDlgButton(IDC_USE_PDH_RADIO, TRUE); } else if (m_data.cpu_usage_acquire_method == GeneralSettingData::CA_HARDWARE_MONITOR) { if (m_data.IsHardwareEnable(HI_CPU)) CheckDlgButton(IDC_USE_HARDWARE_MONITOR_RADIO, TRUE); else CheckDlgButton(IDC_USE_CPU_TIME_RADIO, TRUE); } #ifndef WITHOUT_TEMPERATURE EnableDlgCtrl(IDC_USE_HARDWARE_MONITOR_RADIO, m_data.IsHardwareEnable(HI_CPU)); #else EnableDlgCtrl(IDC_USE_HARDWARE_MONITOR_RADIO, false); #endif m_monitor_span_edit.SetRange(MONITOR_TIME_SPAN_MIN, MONITOR_TIME_SPAN_MAX, MONITOR_SPAN_STEP); m_monitor_span_edit.SetValue(m_data.monitor_time_span); m_monitor_time_span_ori = m_data.monitor_time_span; m_update_source_ori = m_data.update_source; if (CTrafficMonitorDlg::Instance()->IsGetDiskUsageByPdh()) { const auto& disk_names = CTrafficMonitorDlg::Instance()->GetPdhDiskUsageHelper().GetDiskNames(); for (const auto& hdd_name : disk_names) m_hard_disk_combo.AddString(hdd_name); int cur_index = m_hard_disk_combo.FindString(-1, m_data.hard_disk_name.c_str()); m_hard_disk_combo.SetCurSel(cur_index); } #ifndef WITHOUT_TEMPERATURE //初始化硬件监控Check box CheckDlgButton(IDC_CPU_CHECK, m_data.IsHardwareEnable(HI_CPU)); CheckDlgButton(IDC_GPU_CHECK, m_data.IsHardwareEnable(HI_GPU)); CheckDlgButton(IDC_HDD_CHECK, m_data.IsHardwareEnable(HI_HDD)); CheckDlgButton(IDC_MBD_CHECK, m_data.IsHardwareEnable(HI_MBD)); if (theApp.m_pMonitor != nullptr) { CSingleLock sync(&theApp.m_minitor_lib_critical, TRUE); //初始化选择硬盘下拉列表 if (!CTrafficMonitorDlg::Instance()->IsGetDiskUsageByPdh()) { for (const auto& hdd_item : theApp.m_pMonitor->AllHDDTemperature()) m_hard_disk_combo.AddString(hdd_item.first.c_str()); int cur_index = m_hard_disk_combo.FindString(-1, m_data.hard_disk_name.c_str()); m_hard_disk_combo.SetCurSel(cur_index); } //初始化选择CPU下拉列表 m_select_cpu_combo.AddString(CCommon::LoadText(IDS_AVREAGE_TEMPERATURE)); for (const auto& cpu_item : theApp.m_pMonitor->AllCpuTemperature()) m_select_cpu_combo.AddString(cpu_item.first.c_str()); int cur_index = m_select_cpu_combo.FindString(-1, m_data.cpu_core_name.c_str()); if (cur_index < 0) cur_index = 0; m_select_cpu_combo.SetCurSel(cur_index); } #endif //不含温度监控的版本,禁用温度相关的控件 #ifdef WITHOUT_TEMPERATURE EnableDlgCtrl(IDC_CPU_TEMP_TIP_CHECK, false); EnableDlgCtrl(IDC_CPU_TEMP_TIP_EDIT, false); EnableDlgCtrl(IDC_GPU_TEMP_TIP_CHECK, false); EnableDlgCtrl(IDC_GPU_TEMP_TIP_EDIT, false); EnableDlgCtrl(IDC_HDD_TEMP_TIP_CHECK, false); EnableDlgCtrl(IDC_HDD_TIP_EDIT, false); EnableDlgCtrl(IDC_MBD_TEMP_TIP_CHECK, false); EnableDlgCtrl(IDC_MBD_TEMP_TIP_EDIT, false); EnableDlgCtrl(IDC_CPU_CHECK, false); EnableDlgCtrl(IDC_GPU_CHECK, false); EnableDlgCtrl(IDC_HDD_CHECK, false); EnableDlgCtrl(IDC_MBD_CHECK, false); //EnableDlgCtrl(IDC_SELECT_HARD_DISK_COMBO, false); EnableDlgCtrl(IDC_SELECT_CPU_COMBO, false); EnableDlgCtrl(IDC_CPU_TEMP_STATIC, false); EnableDlgCtrl(IDC_GPU_TEMP_STATIC, false); EnableDlgCtrl(IDC_HDD_STATIC, false); EnableDlgCtrl(IDC_MBD_TEMP_STATIC, false); //EnableDlgCtrl(IDC_SELECT_HDD_STATIC, false); EnableDlgCtrl(IDC_SELECT_CPU_STATIC, false); EnableDlgCtrl(IDC_HARDWARE_MONITOR_STATIC, false); #endif m_plugin_manager_btn.SetIcon(theApp.GetMenuIcon(IDI_PLUGINS)); m_select_connection_btn.SetIcon(theApp.GetMenuIcon(IDI_CONNECTION)); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CGeneralSettingsDlg::OnBnClickedCheckNowButton() { // TODO: 在此添加控件通知处理程序代码 theApp.CheckUpdateInThread(true); } void CGeneralSettingsDlg::OnBnClickedCheckUpdateCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.check_update_when_start = (((CButton*)GetDlgItem(IDC_CHECK_UPDATE_CHECK))->GetCheck() != 0); } void CGeneralSettingsDlg::OnBnClickedAutoRunCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.auto_run = (((CButton*)GetDlgItem(IDC_AUTO_RUN_CHECK))->GetCheck() != 0); m_auto_run_modified = true; } void CGeneralSettingsDlg::OnOK() { // TODO: 在此添加专用代码和/或调用基类 //获取消息提示的设置 m_data.traffic_tip_value = m_traffic_tip_edit.GetValue(); if (m_data.traffic_tip_value < 1) m_data.traffic_tip_value = 1; if (m_data.traffic_tip_value > 32767) m_data.traffic_tip_value = 32767; m_data.traffic_tip_unit = m_traffic_tip_combo.GetCurSel(); auto checkTipValue = [](int& value) { if (value < 1) value = 1; if (value > 100) value = 100; }; auto checkTempTipValue = [](int& value) { if (value < 1) value = 1; if (value > 120) value = 120; }; m_data.memory_usage_tip.tip_value = m_memory_tip_edit.GetValue(); checkTipValue(m_data.memory_usage_tip.tip_value); m_data.cpu_temp_tip.tip_value = m_cpu_temp_tip_edit.GetValue(); checkTempTipValue(m_data.cpu_temp_tip.tip_value); m_data.gpu_temp_tip.tip_value = m_gpu_temp_tip_edit.GetValue(); checkTempTipValue(m_data.gpu_temp_tip.tip_value); m_data.hdd_temp_tip.tip_value = m_hdd_temp_tip_edit.GetValue(); checkTempTipValue(m_data.hdd_temp_tip.tip_value); m_data.mainboard_temp_tip.tip_value = m_mbd_temp_tip_edit.GetValue(); checkTempTipValue(m_data.mainboard_temp_tip.tip_value); //获取语言的设置 m_data.language = 0; if (m_language_combo.GetCurSel() > 0) { //选择的不是“跟随系统” int current_language_index = m_language_combo.GetCurSel() - 1; if (current_language_index >= 0 && current_language_index < static_cast(theApp.m_str_table.GetLanguageList().size())) { m_data.language = theApp.m_str_table.GetLanguageList()[current_language_index].language_id; } } if (m_data.language != theApp.m_general_data.language) { MessageBox(CCommon::LoadText(IDS_LANGUAGE_CHANGE_INFO), NULL, MB_ICONINFORMATION | MB_OK); } m_show_all_interface_modified = (m_data.show_all_interface != theApp.m_general_data.show_all_interface); //获取数据文件保存位置的设置 m_data.portable_mode = (((CButton*)GetDlgItem(IDC_SAVE_TO_PROGRAM_DIR_RADIO))->GetCheck() != 0); if (m_data.portable_mode != theApp.m_general_data.portable_mode) { MessageBox(CCommon::LoadText(IDS_CFG_DIR_CHANGED_INFO), NULL, MB_ICONINFORMATION | MB_OK); } //m_taskbar_item_modified = (theApp.m_taskbar_data.display_item != taskbar_displat_item_ori); CTabDlg::OnOK(); } void CGeneralSettingsDlg::OnBnClickedTodayTrafficTipCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.traffic_tip_enable = (((CButton*)GetDlgItem(IDC_TODAY_TRAFFIC_TIP_CHECK))->GetCheck() != 0); SetControlEnable(); } void CGeneralSettingsDlg::OnBnClickedMemoryUsageTipCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.memory_usage_tip.enable = (((CButton*)GetDlgItem(IDC_MEMORY_USAGE_TIP_CHECK))->GetCheck() != 0); SetControlEnable(); } void CGeneralSettingsDlg::OnBnClickedOpenConfigPathButton() { // TODO: 在此添加控件通知处理程序代码 ShellExecute(NULL, _T("explore"), theApp.m_config_dir.c_str(), NULL, NULL, SW_SHOWNORMAL); } void CGeneralSettingsDlg::OnBnClickedShowAllConnectionCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.show_all_interface = (((CButton*)GetDlgItem(IDC_SHOW_ALL_CONNECTION_CHECK))->GetCheck() != 0); SetControlEnable(); } BOOL CGeneralSettingsDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_MOUSEMOVE) m_toolTip.RelayEvent(pMsg); return CTabDlg::PreTranslateMessage(pMsg); } void CGeneralSettingsDlg::OnBnClickedUseCpuTimeRadio() { m_data.cpu_usage_acquire_method = GeneralSettingData::CA_CPU_TIME; } void CGeneralSettingsDlg::OnBnClickedUsePdhRadio() { m_data.cpu_usage_acquire_method = GeneralSettingData::CA_PDH; } void CGeneralSettingsDlg::OnBnClickedUseHardwareMonitorRadio() { m_data.cpu_usage_acquire_method = GeneralSettingData::CA_HARDWARE_MONITOR; } afx_msg LRESULT CGeneralSettingsDlg::OnSpinEditPosChanged(WPARAM wParam, LPARAM lParam) { CSpinButtonCtrl* pSpin = (CSpinButtonCtrl*)wParam; if (pSpin == nullptr) return 0; CWnd* pEdit = pSpin->GetBuddy(); if (pEdit == &m_monitor_span_edit) //当用户点击了“监控时间间隔”的微调按钮时 { LPNMUPDOWN pNMUpDown = reinterpret_cast(lParam); if (pNMUpDown->iDelta == -1) { // 用户按下了spin控件的向下箭头 int value = m_monitor_span_edit.GetValue(); value -= MONITOR_SPAN_STEP; value /= MONITOR_SPAN_STEP; value *= MONITOR_SPAN_STEP; m_monitor_span_edit.SetValue(value); } else if (pNMUpDown->iDelta == 1) { // 用户按下了spin控件的向上箭头 int value = m_monitor_span_edit.GetValue(); value += MONITOR_SPAN_STEP; value /= MONITOR_SPAN_STEP; value *= MONITOR_SPAN_STEP; m_monitor_span_edit.SetValue(value); } pNMUpDown->iDelta = 0; } return 0; } void CGeneralSettingsDlg::OnEnKillfocusMonitorSpanEdit() { // TODO: 在此添加控件通知处理程序代码 //这里限制监控时间间隔只能输入100的倍数 CString str; GetDlgItemText(IDC_MONITOR_SPAN_EDIT, str); str.Replace(_T(","), _T("")); int value = _ttoi(str.GetString()); if (value < MONITOR_TIME_SPAN_MIN || value > MONITOR_TIME_SPAN_MAX) { value = 1000; } else { value /= MONITOR_SPAN_STEP; value *= MONITOR_SPAN_STEP; } m_monitor_span_edit.SetValue(value); } void CGeneralSettingsDlg::OnBnClickedCpuTempTipCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.cpu_temp_tip.enable = (IsDlgButtonChecked(IDC_CPU_TEMP_TIP_CHECK) != 0); SetControlEnable(); } void CGeneralSettingsDlg::OnBnClickedGpuTempTipCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.gpu_temp_tip.enable = (IsDlgButtonChecked(IDC_GPU_TEMP_TIP_CHECK) != 0); SetControlEnable(); } void CGeneralSettingsDlg::OnBnClickedHddTempTipCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.hdd_temp_tip.enable = (IsDlgButtonChecked(IDC_HDD_TEMP_TIP_CHECK) != 0); SetControlEnable(); } void CGeneralSettingsDlg::OnBnClickedMbdTempTipCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.mainboard_temp_tip.enable = (IsDlgButtonChecked(IDC_MBD_TEMP_TIP_CHECK) != 0); SetControlEnable(); } void CGeneralSettingsDlg::OnBnClickedGithubRadio() { // TODO: 在此添加控件通知处理程序代码 m_data.update_source = 0; theApp.m_general_data.update_source = 0; //点击“更新源”的单选按钮时,同时更改theApp中的设置,以确保点击“立即检查”按钮时使用选择的更新源更新 } void CGeneralSettingsDlg::OnBnClickedGiteeRadio() { // TODO: 在此添加控件通知处理程序代码 m_data.update_source = 1; theApp.m_general_data.update_source = 1; } void CGeneralSettingsDlg::OnCancel() { // TODO: 在此添加专用代码和/或调用基类 theApp.m_general_data.update_source = m_update_source_ori; //点击“取消”时恢复开始的“更新源”选项 CTabDlg::OnCancel(); } void CGeneralSettingsDlg::OnBnClickedRestoreDefaultTimeSpanButton() { // TODO: 在此添加控件通知处理程序代码 m_monitor_span_edit.SetValue(1000); } void CGeneralSettingsDlg::OnCbnSelchangeSelectHardDiskCombo() { // TODO: 在此添加控件通知处理程序代码 CString hard_disk_name; m_hard_disk_combo.GetWindowText(hard_disk_name); m_data.hard_disk_name = hard_disk_name.GetString(); } void CGeneralSettingsDlg::OnBnClickedCpuCheck() { // TODO: 在此添加控件通知处理程序代码 bool checked = IsDlgButtonChecked(IDC_CPU_CHECK) != 0; if (checked && !ShowHardwareMonitorWarning()) { checked = false; CheckDlgButton(IDC_CPU_CHECK, FALSE); } m_data.SetHardwareEnable(HI_CPU, checked); EnableDlgCtrl(IDC_USE_HARDWARE_MONITOR_RADIO, checked); } void CGeneralSettingsDlg::OnBnClickedGpuCheck() { // TODO: 在此添加控件通知处理程序代码 bool checked = IsDlgButtonChecked(IDC_GPU_CHECK) != 0; if (checked && !ShowHardwareMonitorWarning()) { checked = false; CheckDlgButton(IDC_GPU_CHECK, FALSE); } m_data.SetHardwareEnable(HI_GPU, checked); } void CGeneralSettingsDlg::OnBnClickedHddCheck() { // TODO: 在此添加控件通知处理程序代码 bool checked = IsDlgButtonChecked(IDC_HDD_CHECK) != 0; if (checked && !ShowHardwareMonitorWarning()) { checked = false; CheckDlgButton(IDC_HDD_CHECK, FALSE); } m_data.SetHardwareEnable(HI_HDD, checked); } void CGeneralSettingsDlg::OnBnClickedMbdCheck() { // TODO: 在此添加控件通知处理程序代码 bool checked = IsDlgButtonChecked(IDC_MBD_CHECK) != 0; if (checked && !ShowHardwareMonitorWarning()) { checked = false; CheckDlgButton(IDC_MBD_CHECK, FALSE); } m_data.SetHardwareEnable(HI_MBD, checked); } void CGeneralSettingsDlg::OnCbnSelchangeSelectCpuCombo() { // TODO: 在此添加控件通知处理程序代码 CString cpu_core_name; m_select_cpu_combo.GetWindowText(cpu_core_name); m_data.cpu_core_name = cpu_core_name.GetString(); } void CGeneralSettingsDlg::OnBnClickedPluginManageButton() { // TODO: 在此添加控件通知处理程序代码 CPluginManagerDlg dlg; dlg.DoModal(); } void CGeneralSettingsDlg::OnBnClickedShowNotifyIconCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.show_notify_icon = (IsDlgButtonChecked(IDC_SHOW_NOTIFY_ICON_CHECK) != 0); } void CGeneralSettingsDlg::OnBnClickedSelectConnectionsButton() { // TODO: 在此添加控件通知处理程序代码 CSelectConnectionsDlg dlg(m_data.connections_hide); if (dlg.DoModal() == IDOK) { m_data.connections_hide = dlg.GetData(); } } void CGeneralSettingsDlg::OnBnClickedResetAutoRunButton() { //先删除开机自动运行 theApp.SetAutoRunByRegistry(false); theApp.SetAutoRunByTaskScheduler(false); if (!theApp.SetAutoRun(true)) //重新设置开机自动运行 { MessageBox(CCommon::LoadText(IDS_SET_AUTO_RUN_FAILED_WARNING), NULL, MB_ICONWARNING | MB_OK); return; } //获取开机自动运行的路径 bool auto_run = theApp.GetAutoRun(&m_auto_run_path); //重新勾选“开机自动运行”复选框 CheckDlgButton(IDC_AUTO_RUN_CHECK, auto_run); //更新鼠标提示 AddOrUpdateAutoRunTooltip(false); } void CGeneralSettingsDlg::OnEnChangeMonitorSpanEdit() { m_data.monitor_time_span = m_monitor_span_edit.GetValue(); } ================================================ FILE: TrafficMonitor/GeneralSettingsDlg.h ================================================ #pragma once #include "TabDlg.h" #include "SpinEdit.h" #include "ComboBox2.h" // CGeneralSettingsDlg dialog class CGeneralSettingsDlg : public CTabDlg { DECLARE_DYNAMIC(CGeneralSettingsDlg) public: CGeneralSettingsDlg(CWnd* pParent = NULL); // standard constructor virtual ~CGeneralSettingsDlg(); static void CheckTaskbarDisplayItem(); //选项设置数据 GeneralSettingData m_data; // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_GENERAL_SETTINGS_DIALOG }; #endif public: bool IsAutoRunModified() const { return m_auto_run_modified; } bool IsShowAllInterfaceModified() const { return m_show_all_interface_modified; } bool IsMonitorTimeSpanModified() const; //bool IsTaskbarItemModified() const { return m_taskbar_item_modified; } protected: bool m_auto_run_modified{ false }; //如果更改了开机自动运行的设置,则会置为true bool m_show_all_interface_modified{ false }; int m_monitor_time_span_ori{}; int m_update_source_ori{}; //bool m_taskbar_item_modified{ false }; wstring m_auto_run_path; //控件变量 CSpinEdit m_traffic_tip_edit; CComboBox2 m_traffic_tip_combo; CSpinEdit m_memory_tip_edit; CComboBox2 m_language_combo; CToolTipCtrl m_toolTip; CSpinEdit m_monitor_span_edit; CSpinEdit m_cpu_temp_tip_edit; CSpinEdit m_gpu_temp_tip_edit; CSpinEdit m_hdd_temp_tip_edit; CSpinEdit m_mbd_temp_tip_edit; CComboBox2 m_hard_disk_combo; CComboBox2 m_select_cpu_combo; CButton m_plugin_manager_btn; CButton m_select_connection_btn; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //设置控件的启用和禁用 void SetControlEnable(); virtual void SetControlMouseWheelEnable(bool enable) override; virtual void OnSettingsApplied() override; virtual bool InitializeControls() override; //显示开启硬件监控时的提示,如果用户选择了“是”则返回true,否则返回false //“以后不再显示该对话框”的标记保存在注册表“\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain” bool ShowHardwareMonitorWarning(); //添加或更新开机自动运行的鼠标提示 void AddOrUpdateAutoRunTooltip(bool add); DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnBnClickedCheckNowButton(); afx_msg void OnBnClickedCheckUpdateCheck(); afx_msg void OnBnClickedAutoRunCheck(); virtual void OnOK(); afx_msg void OnBnClickedTodayTrafficTipCheck(); afx_msg void OnBnClickedMemoryUsageTipCheck(); afx_msg void OnBnClickedOpenConfigPathButton(); afx_msg void OnBnClickedShowAllConnectionCheck(); virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg void OnBnClickedUseCpuTimeRadio(); afx_msg void OnBnClickedUsePdhRadio(); afx_msg void OnBnClickedUseHardwareMonitorRadio(); afx_msg LRESULT OnSpinEditPosChanged(WPARAM wParam, LPARAM lParam); afx_msg void OnEnKillfocusMonitorSpanEdit(); afx_msg void OnBnClickedCpuTempTipCheck(); afx_msg void OnBnClickedGpuTempTipCheck(); afx_msg void OnBnClickedHddTempTipCheck(); afx_msg void OnBnClickedMbdTempTipCheck(); afx_msg void OnBnClickedGithubRadio(); afx_msg void OnBnClickedGiteeRadio(); virtual void OnCancel(); afx_msg void OnBnClickedRestoreDefaultTimeSpanButton(); afx_msg void OnCbnSelchangeSelectHardDiskCombo(); afx_msg void OnBnClickedCpuCheck(); afx_msg void OnBnClickedGpuCheck(); afx_msg void OnBnClickedHddCheck(); afx_msg void OnBnClickedMbdCheck(); afx_msg void OnCbnSelchangeSelectCpuCombo(); afx_msg void OnBnClickedPluginManageButton(); afx_msg void OnBnClickedShowNotifyIconCheck(); afx_msg void OnBnClickedSelectConnectionsButton(); afx_msg void OnBnClickedResetAutoRunButton(); afx_msg void OnEnChangeMonitorSpanEdit(); protected: }; ================================================ FILE: TrafficMonitor/HResultException.cpp ================================================ #include "stdafx.h" #include "HResultException.h" #include "TrafficMonitor.h" namespace FunctionChecker { namespace Details { template bool StrategicCheck(LPCTSTR p_library_name, Strategy strategy) { bool result = false; auto hmodule = ::LoadLibrary(p_library_name); if (hmodule) { result = strategy(hmodule); ::FreeLibrary(hmodule); } return result; } } } // namespace FunctionChecker bool FunctionChecker::CheckLibraryExist(LPCTSTR p_library_name) noexcept { return Details::StrategicCheck(p_library_name, [](HMODULE) { return true; }); } bool FunctionChecker::CheckFunctionExist(LPCTSTR p_library_name, LPCSTR p_function_name) noexcept { return Details::StrategicCheck(p_library_name, [p_function_name](HMODULE h_library) -> bool { auto* p_test_function = ::GetProcAddress(h_library, p_function_name); return p_test_function != NULL; }); } const char* const ERROR_WHEN_CALL_COM_FUNCTION = "Error occurred when call COM function."; void ThrowIfFailed(HRESULT hr, const char* p_message) { ThrowIfFailed(hr, std::move(p_message)); } CHResultException::CHResultException(HRESULT hr, const char* p_message) : std::runtime_error{p_message}, m_hr{hr}, m_get_p_error_hr{::GetErrorInfo(0, &m_p_error)} { } auto CHResultException::GetError() -> Microsoft::WRL::ComPtr { return m_p_error; } bool CHResultException::HasError() const noexcept { return SUCCEEDED(m_hr); } auto CHResultException::GetHResult() const noexcept -> HRESULT { return m_hr; } void LogHResultException(CHResultException& ex) { auto str_hr = "HResult:" + std::to_string(ex.GetHResult()); CCommon::WriteLog(str_hr.c_str(), theApp.m_log_path.c_str()); auto* log = ex.what(); CCommon::WriteLog(log, theApp.m_log_path.c_str()); auto* p_error = ex.GetError().Get(); if (p_error == NULL) { return; } BSTR p_description{NULL}; ThrowIfFailed(p_error->GetDescription(&p_description), TRAFFICMONITOR_ERROR_STR("Get description from IErrorInfo failed.")); CCommon::WriteLog(p_description, theApp.m_log_path.c_str()); ::SysFreeString(p_description); } ================================================ FILE: TrafficMonitor/HResultException.h ================================================ #pragma once #include #include #define TRAFFICMONITOR_STR_IMPL(x) #x #define TRAFFICMONITOR_STR(x) TRAFFICMONITOR_STR_IMPL(x) #define TRAFFICMONITOR_ERROR_STR(x) \ "At file: " TRAFFICMONITOR_STR(__FILE__) " | line: " TRAFFICMONITOR_STR(__LINE__) ".\nError: " x #define RELEASE_COM(p) \ { \ if (p) \ { \ (p)->Release(); \ (p) = (NULL); \ } \ } namespace FunctionChecker { bool CheckLibraryExist(LPCTSTR p_library_name) noexcept; bool CheckFunctionExist(LPCTSTR p_library_name, LPCSTR p_function_name) noexcept; } class CHResultException : public std::runtime_error { public: CHResultException(HRESULT hr, const char* p_message); ~CHResultException() override = default; auto GetError() -> Microsoft::WRL::ComPtr; bool HasError() const noexcept; auto GetHResult() const noexcept -> HRESULT; private: Microsoft::WRL::ComPtr m_p_error; HRESULT m_hr; HRESULT m_get_p_error_hr; }; extern const char* const ERROR_WHEN_CALL_COM_FUNCTION; /*The content should be "Error occurred when call COM function." */ void ThrowIfFailed(HRESULT hr, const char* p_message = ERROR_WHEN_CALL_COM_FUNCTION); template void ThrowIfFailed(HRESULT hr, const char* p_message, Args... args) { if (FAILED(hr)) { throw Exception{hr, p_message, std::forward(args)...}; } } void LogHResultException(CHResultException& ex); ================================================ FILE: TrafficMonitor/HighResolutionTimer.h ================================================ #include #pragma comment(lib, "WINMM.LIB") class CHighResolutionTimer { typedef void(*TIMERCALLBACK)(DWORD_PTR); private: DWORD_PTR m_dwUser; MMRESULT m_nIDTimer; UINT m_uDelay; TIMERCALLBACK m_pfnCallback; public: void KillTimer() { if (m_nIDTimer != NULL) { timeKillEvent(m_nIDTimer); m_nIDTimer = NULL; } } CHighResolutionTimer() { m_nIDTimer = NULL; m_uDelay = 0; m_pfnCallback = 0; } virtual ~CHighResolutionTimer() { KillTimer(); } static void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) { // dwUser contains ptr to Timer object CHighResolutionTimer * ptimer = (CHighResolutionTimer *)dwUser; // Call user-specified callback and pass back user specified data (ptimer->m_pfnCallback) (ptimer->m_dwUser); } BOOL CreateTimer(DWORD_PTR dwUser, UINT uDelay, TIMERCALLBACK lpTimeProc) { ASSERT(dwUser); ASSERT(lpTimeProc); m_dwUser = dwUser; m_pfnCallback = lpTimeProc; BOOL ret = FALSE; KillTimer(); m_nIDTimer = timeSetEvent(uDelay, 0, (LPTIMECALLBACK)TimeProc, (DWORD_PTR)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); if (m_nIDTimer != NULL) ret = TRUE; return(ret); } }; ================================================ FILE: TrafficMonitor/HistoryTrafficCalendarDlg.cpp ================================================ // HistoryTrafficCalendarDlg.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "HistoryTrafficCalendarDlg.h" #include "afxdialogex.h" // CHistoryTrafficCalendarDlg 对话框 IMPLEMENT_DYNAMIC(CHistoryTrafficCalendarDlg, CTabDlg) CHistoryTrafficCalendarDlg::CHistoryTrafficCalendarDlg(deque& history_traffics, CWnd* pParent /*=nullptr*/) : CTabDlg(IDD_HISTORY_TRAFFIC_CALENDAR_DIALOG, pParent), m_history_traffics(history_traffics) { } CHistoryTrafficCalendarDlg::~CHistoryTrafficCalendarDlg() { } void CHistoryTrafficCalendarDlg::SetDayTraffic() { for (int i{}; i < CALENDAR_HEIGHT; i++) { for (int j{}; j < CALENDAR_WIDTH; j++) { HistoryTraffic history_traffic; history_traffic.year = m_year; history_traffic.month = m_month; history_traffic.day = m_calendar[i][j].day; //使用二分法查找日历中每一个日期的流量 if (history_traffic.day > 0) { if (std::binary_search(m_history_traffics.begin(), m_history_traffics.end(), history_traffic, HistoryTraffic::DateGreater)) { auto iter = std::lower_bound(m_history_traffics.begin(), m_history_traffics.end(), history_traffic, HistoryTraffic::DateGreater); if (iter != m_history_traffics.end()) { m_calendar[i][j].up_traffic = iter->up_kBytes; m_calendar[i][j].down_traffic = iter->down_kBytes; m_calendar[i][j].mixed = iter->mixed; } } } } } } void CHistoryTrafficCalendarDlg::MonthSelectChanged() { CCalendarHelper::GetCalendar(m_year, m_month, m_calendar, theApp.m_cfg_data.m_sunday_first); SetDayTraffic(); CalculateMonthTotalTraffic(); InvalidateRect(m_draw_rect); } void CHistoryTrafficCalendarDlg::CalculateMonthTotalTraffic() { m_month_total_upload = 0; m_month_total_download = 0; for (int i{}; i < CALENDAR_HEIGHT; i++) { for (int j{}; j < CALENDAR_WIDTH; j++) { m_month_total_upload += m_calendar[i][j].up_traffic; m_month_total_download += m_calendar[i][j].down_traffic; } } } void CHistoryTrafficCalendarDlg::SetComboSel() { int cnt{}; int year_selected; for (int i{ m_year_max }; i >= m_year_min; i--) { if (i == m_year) year_selected = cnt; cnt++; } m_year_combo.SetCurSel(year_selected); m_month_combo.SetCurSel(m_month - 1); } void CHistoryTrafficCalendarDlg::DoDataExchange(CDataExchange* pDX) { CTabDlg::DoDataExchange(pDX); DDX_Control(pDX, IDC_YEAR_COMBO, m_year_combo); DDX_Control(pDX, IDC_MONTH_COMBO, m_month_combo); } bool CHistoryTrafficCalendarDlg::IsWeekend(int index) { if (theApp.m_cfg_data.m_sunday_first) return (index == 0 || index == 6); else return (index == 5 || index == 6); } CString CHistoryTrafficCalendarDlg::GetWeekdayString(int index) { if (!theApp.m_cfg_data.m_sunday_first) { index++; if (index > 6) index = 0; } switch (index) { case 0: return CCommon::LoadText(IDS_SUNDAY); case 1: return CCommon::LoadText(IDS_MONDAY); case 2: return CCommon::LoadText(IDS_TUESDAY); case 3: return CCommon::LoadText(IDS_WEDNESDAY); case 4: return CCommon::LoadText(IDS_THURSDAY); case 5: return CCommon::LoadText(IDS_FRIDAY); case 6: return CCommon::LoadText(IDS_SATURDAY); } return CString(); } BEGIN_MESSAGE_MAP(CHistoryTrafficCalendarDlg, CTabDlg) ON_WM_PAINT() ON_CBN_SELCHANGE(IDC_YEAR_COMBO, &CHistoryTrafficCalendarDlg::OnCbnSelchangeYearCombo) ON_CBN_SELCHANGE(IDC_MONTH_COMBO, &CHistoryTrafficCalendarDlg::OnCbnSelchangeMonthCombo) ON_WM_MOUSEMOVE() ON_BN_CLICKED(IDC_PREVIOUS_BUTTON, &CHistoryTrafficCalendarDlg::OnBnClickedPreviousButton) ON_BN_CLICKED(IDC_NEXT_BUTTON, &CHistoryTrafficCalendarDlg::OnBnClickedNextButton) ON_WM_MOUSEWHEEL() ON_BN_CLICKED(IDC_MENU_BUTTON, &CHistoryTrafficCalendarDlg::OnBnClickedMenuButton) ON_WM_INITMENU() ON_COMMAND(ID_FIRST_DAY_OF_WEEK_SUNDAY, &CHistoryTrafficCalendarDlg::OnFirstDayOfWeekSunday) ON_COMMAND(ID_FIRST_DAY_OF_WEEK_MONDAY, &CHistoryTrafficCalendarDlg::OnFirstDayOfWeekMonday) ON_COMMAND(ID_CALENDAR_JUMP_TO_TODAY, &CHistoryTrafficCalendarDlg::OnCalendarJumpToToday) END_MESSAGE_MAP() // CHistoryTrafficCalendarDlg 消息处理程序 BOOL CHistoryTrafficCalendarDlg::OnInitDialog() { CTabDlg::OnInitDialog(); // TODO: 在此添加额外的初始化 m_year = m_history_traffics[0].year; m_month = m_history_traffics[0].month; CCalendarHelper::GetCalendar(m_year, m_month, m_calendar, theApp.m_cfg_data.m_sunday_first); SetDayTraffic(); CalculateMonthTotalTraffic(); //初始化Combo Box m_year_max = m_history_traffics[0].year; m_year_min = m_history_traffics.back().year; for (int i{ m_year_max }; i >= m_year_min; i--) { m_year_combo.AddString(CCommon::IntToString(i)); } m_year_combo.SetCurSel(0); for (int i{ 1 }; i <= 12; i++) { m_month_combo.AddString(CCommon::IntToString(i)); } m_month_combo.SetCurSel(m_month - 1); //初始化鼠标提示 m_tool_tips.Create(this, TTS_ALWAYSTIP | TTS_NOPREFIX); m_tool_tips.SetMaxTipWidth(800); //为鼠标提示设置一个最大宽度,以允许其换行 m_tool_tips.AddTool(this, _T("")); CCommon::LoadMenuResource(m_menu, IDR_HISTORY_TRAFFIC_MENU); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CHistoryTrafficCalendarDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CTabDlg::OnPaint() CRect wndRect; GetWindowRect(wndRect); const int width = theApp.DPI(40); //日历中每一个格子的宽度 const int height = theApp.DPI(30); //日历中第一个格子的高度 m_draw_rect.left = m_start_x; m_draw_rect.top = m_start_y; //m_draw_rect.right = m_draw_rect.left + (CALENDAR_WIDTH*width); m_draw_rect.right = wndRect.Width() - m_start_x; m_draw_rect.bottom = m_draw_rect.top + ((CALENDAR_HEIGHT + 2) * height) + theApp.DPI(20); //使用双缓冲绘图 CDrawDoubleBuffer draw_double_buffer(&dc, m_draw_rect); //绘图 CDrawCommon draw; draw.Create(draw_double_buffer.GetMemDC(), this); draw.FillRect(CRect(0, 0, m_draw_rect.Width(), m_draw_rect.Height()), RGB(255, 255, 255)); //填充白色背景色 CRect rect{}; rect.left = 0; rect.top = 0; rect.right = rect.left + width; rect.bottom = rect.top + height; //画星期的行 for (int i{}; i < CALENDAR_WIDTH; i++) { rect.MoveToX(i * width); if (IsWeekend(i)) draw.SetBackColor(RGB(217, 86, 86)); else if (i % 2 == 0) draw.SetBackColor(RGB(1, 84, 151)); else draw.SetBackColor(RGB(1, 107, 191)); CString str = GetWeekdayString(i); draw.DrawWindowText(rect, str, RGB(255, 255, 255), IDrawCommon::Alignment::CENTER, true); } //绘制日历 for (int i{}; i < CALENDAR_HEIGHT; i++) { for (int j{}; j < CALENDAR_WIDTH; j++) { //设置日历中每一天的矩形的位置 rect.MoveToXY(j * width, height + theApp.DPI(2) + i * height); //保存矩形的位置 m_calendar[i][j].rect = rect; //绘制日期的数据 //绘制格子的背景颜色 if (IsWeekend(j)) //是周末时 { if ((i + j) % 2 == 0) draw.SetBackColor(RGB(250, 234, 234)); else draw.SetBackColor(RGB(252, 242, 242)); } else { if ((i + j) % 2 == 0) draw.SetBackColor(RGB(226, 241, 254)); else draw.SetBackColor(RGB(236, 246, 254)); } draw.FillRectWithBackColor(rect); //绘制格子上的日期的数字 CRect day_rect{ rect }; day_rect.bottom -= (rect.Height() / 2); COLORREF text_color; if (IsWeekend(j)) text_color = RGB(131, 29, 28); else text_color = RGB(0, 57, 107); if (m_calendar[i][j].day != 0) draw.DrawWindowText(day_rect, CCommon::IntToString(m_calendar[i][j].day), text_color, IDrawCommon::Alignment::CENTER, true); //在今天的日期上画一个矩形框 COLORREF frame_color; if (IsWeekend(j)) frame_color = RGB(218, 91, 91); else frame_color = RGB(1, 133, 238); if (m_year == m_history_traffics[0].year && m_month == m_history_traffics[0].month && m_calendar[i][j].day == m_history_traffics[0].day) draw.DrawRectOutLine(rect, frame_color, theApp.DPI(2)); //绘制指示流量大小的矩形 COLORREF color; if (m_calendar[i][j].traffic() < 1024 * 1024) //流量小于1GB时绘制蓝色 color = TRAFFIC_COLOR_BLUE; else if (m_calendar[i][j].traffic() < 10 * 1024 * 1024) //流量小于10GB时绘制绿色 color = TRAFFIC_COLOR_GREEN; else if (m_calendar[i][j].traffic() < 100 * 1024 * 1024) //流量小于100GB时绘制黄色 color = TRAFFIC_COLOR_YELLOE; else if (m_calendar[i][j].traffic() < 1024 * 1024 * 1024) //流量小于1TB时绘制红色 color = TRAFFIC_COLOR_RED; else //流量超过1TB时显示深红色 color = TRAFFIC_COLOR_DARK_RED; if (m_calendar[i][j].traffic() > 0) { CRect traffic_rect; traffic_rect.left = rect.left + theApp.DPI(14); traffic_rect.right = traffic_rect.left + theApp.DPI(12); traffic_rect.top = rect.top + theApp.DPI(16); traffic_rect.bottom = traffic_rect.top + theApp.DPI(12); draw.FillRect(traffic_rect, color); } } } const COLORREF text_color{ RGB(0, 57, 107) }; //画当前月总流量 CString info; info.Format(_T("%s %s (%s: %s, %s: %s)"), CCommon::LoadText(IDS_CURRENT_MONTH_TOTAL_TRAFFIC), CCommon::KBytesToString(m_month_total_upload + m_month_total_download), CCommon::LoadText(IDS_UPLOAD), CCommon::KBytesToString(m_month_total_upload), CCommon::LoadText(IDS_DOWNLOAD), CCommon::KBytesToString(m_month_total_download) ); CRect info_rect; info_rect.left = 0; info_rect.top = height * (CALENDAR_HEIGHT + 1) + theApp.DPI(5); info_rect.right = info_rect.left + m_draw_rect.Width(); info_rect.bottom = info_rect.top + theApp.DPI(36); draw.SetBackColor(RGB(255, 255, 255)); draw.DrawWindowText(info_rect, info, text_color, IDrawCommon::Alignment::LEFT, true, true); //画图例 CRect rc_legend{ info_rect }; rc_legend.MoveToXY(CALENDAR_WIDTH * width + theApp.DPI(32), theApp.DPI(16)); rc_legend.right = m_draw_rect.Width(); rc_legend.bottom = rc_legend.top + theApp.DPI(16); draw.DrawWindowText(rc_legend, CCommon::LoadText(IDS_LEGEND, _T(":")), text_color, IDrawCommon::Alignment::LEFT, true, true); rc_legend.MoveToY(rc_legend.bottom + theApp.DPI(6)); CRect rc_legend_box{ rc_legend }; const int box_side{ theApp.DPI(12) }; const int line_gap{ theApp.DPI(6) }; rc_legend_box.top = rc_legend.top + (rc_legend.Height() - box_side) / 2; rc_legend_box.bottom = rc_legend_box.top + box_side; rc_legend_box.right = rc_legend_box.left + box_side; rc_legend.left = rc_legend_box.right + theApp.DPI(4); //蓝色图例 draw.FillRect(rc_legend_box, TRAFFIC_COLOR_BLUE); draw.DrawWindowText(rc_legend, _T("0~1GB"), text_color); //绿色图例 rc_legend.MoveToY(rc_legend.bottom + line_gap); rc_legend_box.MoveToY(rc_legend.top + (rc_legend.Height() - box_side) / 2); draw.FillRect(rc_legend_box, TRAFFIC_COLOR_GREEN); draw.DrawWindowText(rc_legend, _T("1GB~10GB"), text_color); //黄色图例 rc_legend.MoveToY(rc_legend.bottom + line_gap); rc_legend_box.MoveToY(rc_legend.top + (rc_legend.Height() - box_side) / 2); draw.FillRect(rc_legend_box, TRAFFIC_COLOR_YELLOE); draw.DrawWindowText(rc_legend, _T("10GB~100GB"), text_color); //红色图例 rc_legend.MoveToY(rc_legend.bottom + line_gap); rc_legend_box.MoveToY(rc_legend.top + (rc_legend.Height() - box_side) / 2); draw.FillRect(rc_legend_box, TRAFFIC_COLOR_RED); draw.DrawWindowText(rc_legend, _T("100GB~1TB"), text_color); //深红色图例 rc_legend.MoveToY(rc_legend.bottom + line_gap); rc_legend_box.MoveToY(rc_legend.top + (rc_legend.Height() - box_side) / 2); draw.FillRect(rc_legend_box, TRAFFIC_COLOR_DARK_RED); draw.DrawWindowText(rc_legend, _T("1TB~"), text_color); } void CHistoryTrafficCalendarDlg::OnCbnSelchangeYearCombo() { // TODO: 在此添加控件通知处理程序代码 int index = m_year_combo.GetCurSel(); CString str; m_year_combo.GetLBText(index, str); m_year = _ttoi(str); MonthSelectChanged(); } void CHistoryTrafficCalendarDlg::OnCbnSelchangeMonthCombo() { // TODO: 在此添加控件通知处理程序代码 m_month = m_month_combo.GetCurSel() + 1; MonthSelectChanged(); } void CHistoryTrafficCalendarDlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 int tip_day{}; //要显示鼠标提示的日期 DayTraffic tip_traffic{}; static int last_tip_day{}; //查找鼠标指针在哪个日期的矩形内 for (int i{}; i < CALENDAR_HEIGHT; i++) { for (int j{}; j < CALENDAR_WIDTH; j++) { CRect rect{ m_calendar[i][j].rect }; rect.OffsetRect(m_start_x, m_start_y); if (rect.PtInRect(point)) { tip_day = m_calendar[i][j].day; tip_traffic = m_calendar[i][j]; } } } bool show_tip = (tip_day > 0); if (show_tip && last_tip_day != tip_day) { CString tip_info; tip_info.Format(_T("%d/%d/%d\r\n"), m_year, m_month, tip_day); tip_info += CCommon::LoadText(IDS_TRAFFIC_USED1); tip_info += CCommon::KBytesToString(tip_traffic.traffic()); if (!tip_traffic.mixed && tip_traffic.traffic() > 0) { tip_info += _T("\r\n"); tip_info += CCommon::LoadText(IDS_UPLOAD, _T(": ")); tip_info += CCommon::KBytesToString(tip_traffic.up_traffic); tip_info += _T("\r\n"); tip_info += CCommon::LoadText(IDS_DOWNLOAD, _T(": ")); tip_info += CCommon::KBytesToString(tip_traffic.down_traffic); } m_tool_tips.AddTool(this, tip_info); m_tool_tips.Pop(); last_tip_day = tip_day; } if (!show_tip) { m_tool_tips.AddTool(this, _T("")); m_tool_tips.Pop(); last_tip_day = 0; } CTabDlg::OnMouseMove(nFlags, point); } BOOL CHistoryTrafficCalendarDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (m_tool_tips.GetSafeHwnd() != 0) { m_tool_tips.RelayEvent(pMsg); } if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_LEFT) { OnBnClickedPreviousButton(); return TRUE; } if (pMsg->wParam == VK_RIGHT) { OnBnClickedNextButton(); return TRUE; } } return CTabDlg::PreTranslateMessage(pMsg); } void CHistoryTrafficCalendarDlg::OnBnClickedPreviousButton() { // TODO: 在此添加控件通知处理程序代码 if (m_year == m_year_min && m_month == 1) return; m_month--; if (m_month <= 0) { m_month = 12; m_year--; } SetComboSel(); MonthSelectChanged(); } void CHistoryTrafficCalendarDlg::OnBnClickedNextButton() { // TODO: 在此添加控件通知处理程序代码 if (m_year == m_year_max && m_month == 12) return; m_month++; if (m_month > 12) { m_month = 1; m_year++; } SetComboSel(); MonthSelectChanged(); } BOOL CHistoryTrafficCalendarDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //通过鼠标滚轮翻页 if (zDelta > 0) { OnBnClickedPreviousButton(); } if (zDelta < 0) { OnBnClickedNextButton(); } return CTabDlg::OnMouseWheel(nFlags, zDelta, pt); } void CHistoryTrafficCalendarDlg::OnBnClickedMenuButton() { // TODO: 在此添加控件通知处理程序代码 CWnd* pBtn = GetDlgItem(IDC_MENU_BUTTON); if (pBtn != nullptr) { CRect rect; pBtn->GetWindowRect(rect); CPoint point; point.x = rect.left; point.y = rect.bottom; CMenu* pMenu = m_menu.GetSubMenu(1); if (pMenu != NULL) pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); } } void CHistoryTrafficCalendarDlg::OnInitMenu(CMenu* pMenu) { CTabDlg::OnInitMenu(pMenu); // TODO: 在此处添加消息处理程序代码 if (theApp.m_cfg_data.m_sunday_first) pMenu->CheckMenuRadioItem(ID_FIRST_DAY_OF_WEEK_SUNDAY, ID_FIRST_DAY_OF_WEEK_MONDAY, ID_FIRST_DAY_OF_WEEK_SUNDAY, MF_BYCOMMAND | MF_CHECKED); else pMenu->CheckMenuRadioItem(ID_FIRST_DAY_OF_WEEK_SUNDAY, ID_FIRST_DAY_OF_WEEK_MONDAY, ID_FIRST_DAY_OF_WEEK_MONDAY, MF_BYCOMMAND | MF_CHECKED); } void CHistoryTrafficCalendarDlg::OnFirstDayOfWeekSunday() { // TODO: 在此添加命令处理程序代码 theApp.m_cfg_data.m_sunday_first = true; MonthSelectChanged(); } void CHistoryTrafficCalendarDlg::OnFirstDayOfWeekMonday() { // TODO: 在此添加命令处理程序代码 theApp.m_cfg_data.m_sunday_first = false; MonthSelectChanged(); } void CHistoryTrafficCalendarDlg::OnCalendarJumpToToday() { // TODO: 在此添加命令处理程序代码 m_year = m_history_traffics[0].year; m_month = m_history_traffics[0].month; SetComboSel(); MonthSelectChanged(); } ================================================ FILE: TrafficMonitor/HistoryTrafficDlg.cpp ================================================ // HistoryTrafficDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "HistoryTrafficDlg.h" #include "afxdialogex.h" // CHistoryTrafficDlg 对话框 IMPLEMENT_DYNAMIC(CHistoryTrafficDlg, CBaseDialog) CHistoryTrafficDlg::CHistoryTrafficDlg(deque& history_traffics, CWnd* pParent /*=NULL*/) : CBaseDialog(IDD_HISTORY_TRAFFIC_DIALOG, pParent), m_history_traffics(history_traffics), m_tab1_dlg(history_traffics, this), m_tab2_dlg(history_traffics, this) { } CHistoryTrafficDlg::~CHistoryTrafficDlg() { } CString CHistoryTrafficDlg::GetDialogName() const { return _T("HistoryTrafficDlg"); } void CHistoryTrafficDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_TAB1, m_tab); } void CHistoryTrafficDlg::SetTabWndSize() { CRect rect; m_tab.GetClientRect(rect); CRect rcTabItem; m_tab.GetItemRect(0, rcTabItem); rect.top += rcTabItem.Height() + 4; rect.left += 4; rect.bottom -= 4; rect.right -= 4; m_tab1_dlg.MoveWindow(&rect); m_tab2_dlg.MoveWindow(&rect); } BEGIN_MESSAGE_MAP(CHistoryTrafficDlg, CBaseDialog) ON_WM_GETMINMAXINFO() ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CHistoryTrafficDlg::OnTcnSelchangeTab1) ON_WM_SIZE() END_MESSAGE_MAP() // CHistoryTrafficDlg 消息处理程序 BOOL CHistoryTrafficDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_STATISTICS), FALSE); // 设置小图标 //插入标签 m_tab.InsertItem(0, CCommon::LoadText(IDS_LIST_VIEW)); m_tab.InsertItem(1, CCommon::LoadText(IDS_CALENDAR_VIEW)); //创建子对话框 m_tab1_dlg.Create(IDD_HISTORY_TRAFFIC_LIST_DIALOG, &m_tab); m_tab2_dlg.Create(IDD_HISTORY_TRAFFIC_CALENDAR_DIALOG, &m_tab); //调整子对话框的大小和位置 SetTabWndSize(); //设置默认选中的标签 switch (m_tab_selected) { case 0: m_tab1_dlg.ShowWindow(SW_SHOW); break; case 1: m_tab2_dlg.ShowWindow(SW_SHOW); break; } m_tab.SetCurFocus(m_tab_selected); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } BOOL CHistoryTrafficDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 //if (GetKeyState(VK_CONTROL) & 0x80) //{ // if (pMsg->wParam == 'D') // { // HistoryTraffic h{}; // h.year = 2018; // h.month = 4; // h.day = 29; // auto iter = std::lower_bound(m_history_traffics.begin(), m_history_traffics.end(), h, HistoryTraffic::DateGreater); // int index = iter - m_history_traffics.begin(); // } //} return CBaseDialog::PreTranslateMessage(pMsg); } void CHistoryTrafficDlg::OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) { // TODO: 在此添加控件通知处理程序代码 m_tab_selected = m_tab.GetCurSel(); switch (m_tab_selected) { case 0: m_tab1_dlg.ShowWindow(SW_SHOW); m_tab2_dlg.ShowWindow(SW_HIDE); m_tab1_dlg.SetFocus(); break; case 1: m_tab2_dlg.ShowWindow(SW_SHOW); m_tab1_dlg.ShowWindow(SW_HIDE); m_tab2_dlg.SetFocus(); break; } *pResult = 0; } void CHistoryTrafficDlg::OnSize(UINT nType, int cx, int cy) { CBaseDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 if (nType != SIZE_MINIMIZED && m_tab1_dlg.GetSafeHwnd() != NULL && m_tab2_dlg.GetSafeHwnd() != NULL) { SetTabWndSize(); } } ================================================ FILE: TrafficMonitor/HistoryTrafficDlg.h ================================================ #pragma once #include "afxcmn.h" #include "Common.h" #include "HistoryTrafficListDlg.h" #include "HistoryTrafficCalendarDlg.h" #include "BaseDialog.h" // CHistoryTrafficDlg 对话框 class CHistoryTrafficDlg : public CBaseDialog { DECLARE_DYNAMIC(CHistoryTrafficDlg) public: CHistoryTrafficDlg(deque& history_traffics, CWnd* pParent = NULL); // 标准构造函数 virtual ~CHistoryTrafficDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_HISTORY_TRAFFIC_DIALOG }; #endif public: CHistoryTrafficListDlg m_tab1_dlg; CHistoryTrafficCalendarDlg m_tab2_dlg; protected: deque& m_history_traffics; CTabCtrl m_tab; int m_tab_selected; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 void SetTabWndSize(); virtual CString GetDialogName() const override; DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg void OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnSize(UINT nType, int cx, int cy); }; ================================================ FILE: TrafficMonitor/HistoryTrafficFile.cpp ================================================ #include "stdafx.h" #include "HistoryTrafficFile.h" #include "Common.h" CHistoryTrafficFile::CHistoryTrafficFile(const wstring& file_path) : m_file_path(file_path) { } CHistoryTrafficFile::~CHistoryTrafficFile() { } void CHistoryTrafficFile::Save() const { ofstream file{ m_file_path }; char buff[64]; sprintf_s(buff, "lines: \"%u\"", static_cast(m_history_traffics.size())); //ڵһд file << buff << std::endl; for (const auto& history_traffic : m_history_traffics) { if (history_traffic.mixed) sprintf_s(buff, "%.4d/%.2d/%.2d %llu", history_traffic.year, history_traffic.month, history_traffic.day, history_traffic.down_kBytes); else sprintf_s(buff, "%.4d/%.2d/%.2d %llu/%llu", history_traffic.year, history_traffic.month, history_traffic.day, history_traffic.up_kBytes, history_traffic.down_kBytes); file << buff << std::endl; } file.close(); } void CHistoryTrafficFile::Load() { ifstream file{ m_file_path }; string current_line, temp; HistoryTraffic traffic; //bool first_line{ true }; if (CCommon::FileExist(m_file_path.c_str())) { while (!file.eof()) { if (m_history_traffics.size() > 9999) break; //ȡ10000ʷ¼ std::getline(file, current_line); //if (first_line) //{ // first_line = false; // size_t index = current_line.find("lines:"); // if(index != wstring::npos) // { // index = current_line.find("\"", index + 6); // size_t index1 = current_line.find("\"", index + 1); // temp = current_line.substr(index + 1, index1 - index - 1); // m_size = atoll(temp.c_str()); // continue; // } //} if (current_line.size() < 12) continue; temp = current_line.substr(0, 4); traffic.year = atoi(temp.c_str()); if (traffic.year < 1900 || traffic.year > 3000) continue; temp = current_line.substr(5, 2); traffic.month = atoi(temp.c_str()); if (traffic.month < 1 || traffic.month > 12) continue; temp = current_line.substr(8, 2); traffic.day = atoi(temp.c_str()); if (traffic.day < 1 || traffic.day > 31) continue; int index = current_line.find(L'/', 11); traffic.mixed = (index == wstring::npos); if (traffic.mixed) { temp = current_line.substr(11); traffic.down_kBytes = atoll(temp.c_str()); traffic.up_kBytes = 0; } else { temp = current_line.substr(11, index - 11); traffic.up_kBytes = atoll(temp.c_str()); temp = current_line.substr(index + 1); traffic.down_kBytes = atoll(temp.c_str()); } if (traffic.year > 0 && traffic.month > 0 && traffic.day > 0 && traffic.kBytes() > 0) m_history_traffics.push_back(traffic); } } MormalizeData(); } void CHistoryTrafficFile::LoadSize() { ifstream file{ m_file_path }; string current_line, temp; if (CCommon::FileExist(m_file_path.c_str())) { std::getline(file, current_line); //ȡһ size_t index = current_line.find("lines:"); if (index != wstring::npos) { index = current_line.find("\"", index + 6); size_t index1 = current_line.find("\"", index + 1); temp = current_line.substr(index + 1, index1 - index - 1); m_size = atoll(temp.c_str()); } } } void CHistoryTrafficFile::Merge(const CHistoryTrafficFile& history_traffic, bool ignore_same_data) { for (const HistoryTraffic& traffic : history_traffic.m_history_traffics) { if(ignore_same_data) { //Ҫͬڵʹöַͬҵˣ if (std::binary_search(m_history_traffics.begin(), m_history_traffics.end(), traffic, HistoryTraffic::DateGreater)) { auto iter = std::lower_bound(m_history_traffics.begin(), m_history_traffics.end(), traffic, HistoryTraffic::DateGreater); if (iter != m_history_traffics.end()) { continue; } } } m_history_traffics.push_back(traffic); } MormalizeData(); } void CHistoryTrafficFile::MormalizeData() { SYSTEMTIME current_time; GetLocalTime(¤t_time); HistoryTraffic traffic; traffic.year = current_time.wYear; traffic.month = current_time.wMonth; traffic.day = current_time.wDay; traffic.up_kBytes = 0; traffic.down_kBytes = 0; traffic.mixed = false; if (m_history_traffics.empty()) { m_history_traffics.push_front(traffic); } if (m_history_traffics.size() >= 2) { //ȡʷбڴӴС std::sort(m_history_traffics.begin(), m_history_traffics.end(), HistoryTraffic::DateGreater); //бͬڵĿϲ for (int i{}; i < static_cast(m_history_traffics.size() - 1); i++) { if (HistoryTraffic::DateEqual(m_history_traffics[i], m_history_traffics[i + 1])) { m_history_traffics[i].up_kBytes += m_history_traffics[i + 1].up_kBytes; m_history_traffics[i].down_kBytes += m_history_traffics[i + 1].down_kBytes; m_history_traffics.erase(m_history_traffics.begin() + i + 1); } } } //бһĿǽ죬򽫵һĿͳƵΪʹõбǰһΪĿ if (HistoryTraffic::DateEqual(m_history_traffics[0], traffic)) { m_today_up_traffic = static_cast<__int64>(m_history_traffics[0].up_kBytes) * 1024; m_today_down_traffic = static_cast<__int64>(m_history_traffics[0].down_kBytes) * 1024; m_history_traffics[0].mixed = false; } else { m_history_traffics.push_front(traffic); } m_size = m_history_traffics.size(); } ================================================ FILE: TrafficMonitor/HistoryTrafficFile.h ================================================ #pragma once #include "CommonData.h" class CHistoryTrafficFile { public: CHistoryTrafficFile(const wstring& file_path); ~CHistoryTrafficFile(); void Save() const; void Load(); void LoadSize(); //ȡļĴС void Merge(const CHistoryTrafficFile& history_traffic, bool ignore_same_data = false); //ϲһCHistoryTrafficFileignore_same_dataΪtrueͬڵͬڵ const wstring& GetFilePath() const { return m_file_path; } const void SetFilePath(const wstring& file_path) { m_file_path = file_path; } deque& GetTraffics() { return m_history_traffics; } __int64 GetTodayUpTraffic() const { return m_today_up_traffic; } __int64 GetTodayDownTraffic() const { return m_today_down_traffic; } size_t Size() { return m_size; } private: void MormalizeData(); //ʷ򲢺ϲͬ private: wstring m_file_path; deque m_history_traffics; //ʷ __int64 m_today_up_traffic{}; //ʹõϴ __int64 m_today_down_traffic{}; //ʹõ size_t m_size{}; //ݵ }; ================================================ FILE: TrafficMonitor/HistoryTrafficListCtrl.cpp ================================================ #include "stdafx.h" #include "HistoryTrafficListCtrl.h" IMPLEMENT_DYNAMIC(CHistoryTrafficListCtrl, CListCtrl) CHistoryTrafficListCtrl::CHistoryTrafficListCtrl() { } CHistoryTrafficListCtrl::~CHistoryTrafficListCtrl() { } void CHistoryTrafficListCtrl::SetDrawItemRangeData(int item, double range, COLORREF color) { if (item < 0) return; if (item >= static_cast(m_item_rage_data.size())) m_item_rage_data.resize(item + 1); m_item_rage_data[item].data_value = range; m_item_rage_data[item].color = color; } void CHistoryTrafficListCtrl::SetDrawItemRangInLogScale(bool log_scale) { m_use_log_scale = log_scale; Invalidate(); } BEGIN_MESSAGE_MAP(CHistoryTrafficListCtrl, CListCtrl) ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CHistoryTrafficListCtrl::OnNMCustomdraw) END_MESSAGE_MAP() void CHistoryTrafficListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) { if (m_draw_item_range) { *pResult = CDRF_DODEFAULT; LPNMLVCUSTOMDRAW lplvdr = reinterpret_cast(pNMHDR); NMCUSTOMDRAW& nmcd = lplvdr->nmcd; switch (lplvdr->nmcd.dwDrawStage) //判断状态 { case CDDS_PREPAINT: *pResult = CDRF_NOTIFYITEMDRAW; break; case CDDS_ITEMPREPAINT: //如果为画ITEM之前就要进行颜色的改变 if (nmcd.dwItemSpec >= 0 && nmcd.dwItemSpec < m_item_rage_data.size()) { double range = m_item_rage_data[nmcd.dwItemSpec].data_value; CDC* pDC = CDC::FromHandle(nmcd.hdc); //获取绘图DC CRect item_rect, draw_rect; GetSubItemRect(nmcd.dwItemSpec,m_draw_item_range_row, LVIR_BOUNDS, item_rect); //获取绘图单元格的矩形区域 CDrawCommon::SetDrawRect(pDC, item_rect); //设置绘图区域为当前列 //使用双缓冲绘图 { CDrawDoubleBuffer draw_double_buffer(pDC, item_rect); //填充背景 draw_rect = item_rect; draw_rect.MoveToXY(0, 0); draw_double_buffer.GetMemDC()->FillSolidRect(draw_rect, GetSysColor(COLOR_WINDOW)); if (draw_rect.Height() > 2 * m_margin) { draw_rect.top += m_margin; draw_rect.bottom -= m_margin; } int width; if (m_use_log_scale) //使用对数比例(y=ln(x+1)) { range = std::log(range + 1); width = static_cast(range*draw_rect.Width() / std::log(1000 + 1)); } else //使用线性比例(y=x) { width = static_cast(range*draw_rect.Width() / 1000); } draw_rect.right = draw_rect.left + width; draw_double_buffer.GetMemDC()->FillSolidRect(draw_rect, m_item_rage_data[nmcd.dwItemSpec].color); } //当前列绘制完成后将绘图区域设置为左边的区域,防止当前列的区域被覆盖 CRect rect1{ item_rect }; rect1.left = 0; rect1.right = item_rect.left; CDrawCommon::SetDrawRect(pDC, rect1); } *pResult = CDRF_DODEFAULT; break; } } } ================================================ FILE: TrafficMonitor/HistoryTrafficListCtrl.h ================================================ #pragma once #include "afxcmn.h" #include "DrawCommon.h" class CHistoryTrafficListCtrl : public CListCtrl { DECLARE_DYNAMIC(CHistoryTrafficListCtrl) public: CHistoryTrafficListCtrl(); ~CHistoryTrafficListCtrl(); void EnableDrawItemRange(bool draw = true) { m_draw_item_range = draw; } //ǷҪijһеԪлƱʾֵСľ void SetDrawItemRangeRow(int row) { m_draw_item_range_row = row; } //ҪƱʾֵСľεУעбΪұߵУ򣬴ұߵн޷ʾ void SetDrawItemRangeData(int item, double range, COLORREF color); //ijһеĿݴСȡֵΪ1~1000ɫ void SetDrawItemRangMargin(int margin) { m_margin = margin; } //ûƻƵľεıԵԪ߿ľΣֵԽƵľԽϸDzܳбоһ void SetDrawItemRangInLogScale(bool log_scale); //ҪƱʾֵСľʱǷʹöʹԱ protected: struct ItemData { double data_value; //ҪƵľαʾֵСΧΪ0~1000 COLORREF color; //ҪƵľεɫ }; bool m_draw_item_range{ false }; //ǷҪijһеԪлƱʾֵСľ int m_draw_item_range_row{}; //ҪƱʾֵСľε int m_margin{}; vector m_item_rage_data; //ڱʾÿһҪƵݴСͻͼɫ bool m_use_log_scale{ false }; //ΪtrueʹöƾΣʹԱ DECLARE_MESSAGE_MAP() afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult); }; ================================================ FILE: TrafficMonitor/HistoryTrafficListDlg.cpp ================================================ // HistoryTrafficList.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "HistoryTrafficListDlg.h" #include "afxdialogex.h" #include "CalendarHelper.h" // CHistoryTrafficListDlgDlg 对话框 IMPLEMENT_DYNAMIC(CHistoryTrafficListDlg, CTabDlg) CHistoryTrafficListDlg::CHistoryTrafficListDlg(deque& history_traffics, CWnd* pParent /*=nullptr*/) : CTabDlg(IDD_HISTORY_TRAFFIC_LIST_DIALOG, pParent), m_history_traffics(history_traffics) { } CHistoryTrafficListDlg::~CHistoryTrafficListDlg() { } bool CHistoryTrafficListDlg::CalculateColumeWidth(std::vector& widths) { if (!IsWindow(m_history_list.GetSafeHwnd())) return false; CRect rect; m_history_list.GetWindowRect(rect); if (rect.Width() <= 0) return false; const int MAX_COLUME{ 5 }; int width_date; //“日期”列的宽度 int width0; //“上传”、“下载”、“总流量”列的宽度 int width1; //“图表”列的宽度 width_date = rect.Width() * 3 / 11; width0 = rect.Width() * 2 / 11; if (width_date > theApp.DPI(150)) width_date = theApp.DPI(150); if (width0 > theApp.DPI(120)) width0 = theApp.DPI(120); width1 = rect.Width() - (MAX_COLUME - 2) * width0 - width_date - theApp.DPI(20) - 1; widths.resize(MAX_COLUME); widths[0] = width_date; widths[1] = widths[2] = widths[3] = width0; widths[4] = width1; return true; } void CHistoryTrafficListDlg::AddListRow(const ListRowData& data, unsigned __int64 max_traffic) { unsigned __int64 total_kbytes = data.up_kBytes + data.down_kBytes; int index = m_history_list.GetItemCount(); m_history_list.InsertItem(index, data.str); if (data.mixed) { m_history_list.SetItemText(index, 1, _T("-")); m_history_list.SetItemText(index, 2, _T("-")); } else { m_history_list.SetItemText(index, 1, CCommon::KBytesToString(data.up_kBytes)); m_history_list.SetItemText(index, 2, CCommon::KBytesToString(data.down_kBytes)); } m_history_list.SetItemText(index, 3, CCommon::KBytesToString(total_kbytes)); double range = static_cast(total_kbytes) * 1000 / max_traffic; COLORREF color; if (total_kbytes < 1024 * 1024) //流量小于1GB时绘制蓝色 color = TRAFFIC_COLOR_BLUE; else if (total_kbytes < 10 * 1024 * 1024) //流量小于10GB时绘制绿色 color = TRAFFIC_COLOR_GREEN; else if (total_kbytes < 100 * 1024 * 1024) //流量小于100GB时绘制黄色 color = TRAFFIC_COLOR_YELLOE; else if (total_kbytes < 1024 * 1024 * 1024) //流量小于1TB时绘制红色 color = TRAFFIC_COLOR_RED; else //流量超过1TB时显示深红色 color = TRAFFIC_COLOR_DARK_RED; m_history_list.SetDrawItemRangeData(index, range, color); } void CHistoryTrafficListDlg::ShowListData() { m_history_list.DeleteAllItems(); //显示日视图 if(theApp.m_cfg_data.m_view_type == HistoryTrafficViewType::HV_DAY) { //获取历史流量列表中流量的最大值 unsigned __int64 max_traffic{}; for (const auto& traffic : m_history_traffics) { if (traffic.kBytes() > max_traffic) max_traffic = traffic.kBytes(); } for (size_t i{}; i < m_history_traffics.size(); i++) { CString date_str; //CString k_bytes_str; date_str.Format(_T("%.4d/%.2d/%.2d ("), m_history_traffics[i].year, m_history_traffics[i].month, m_history_traffics[i].day); int week_day = CCalendarHelper::CaculateWeekDay(m_history_traffics[i].year, m_history_traffics[i].month, m_history_traffics[i].day); switch (week_day) { case 0: date_str += CCommon::LoadText(IDS_SUNDAY); break; case 1: date_str += CCommon::LoadText(IDS_MONDAY); break; case 2: date_str += CCommon::LoadText(IDS_TUESDAY); break; case 3: date_str += CCommon::LoadText(IDS_WEDNESDAY); break; case 4: date_str += CCommon::LoadText(IDS_THURSDAY); break; case 5: date_str += CCommon::LoadText(IDS_FRIDAY); break; case 6: date_str += CCommon::LoadText(IDS_SATURDAY); break; default: break; } date_str += _T(')'); ListRowData data; data.str = date_str; data.up_kBytes = m_history_traffics[i].up_kBytes; data.down_kBytes = m_history_traffics[i].down_kBytes; data.mixed = m_history_traffics[i].mixed; AddListRow(data, max_traffic); } } //显示月/季/年视图 else { std::vector list_data; unsigned __int64 max_traffic{}; for (const auto& traffic : m_history_traffics) { CString date_str; if (theApp.m_cfg_data.m_view_type == HistoryTrafficViewType::HV_WEEK) { date_str.Format(_T("%.4d/"), traffic.year); date_str += CCommon::LoadTextFormat(IDS_WEEK_NUM, { traffic.week() }); } else if (theApp.m_cfg_data.m_view_type == HistoryTrafficViewType::HV_MONTH) //月视图 { date_str.Format(_T("%.4d/%.2d"), traffic.year, traffic.month); } else if (theApp.m_cfg_data.m_view_type == HistoryTrafficViewType::HV_QUARTER) //季视图 { date_str.Format(_T("%.4d/"), traffic.year); if (traffic.month <= 3) date_str += _T("Q1"); else if (traffic.month <= 6) date_str += _T("Q2"); else if (traffic.month <= 9) date_str += _T("Q3"); else date_str += _T("Q4"); } else //年视图 { date_str.Format(_T("%.4d"), traffic.year); } if (list_data.empty() || list_data.back().str != date_str) { if(!list_data.empty()) { unsigned __int64 cur_traffic{ list_data.back().up_kBytes + list_data.back().down_kBytes }; if (cur_traffic > max_traffic) max_traffic = cur_traffic; } ListRowData data; data.str = date_str; data.up_kBytes = traffic.up_kBytes; data.down_kBytes = traffic.down_kBytes; list_data.push_back(data); } else { list_data.back().up_kBytes += traffic.up_kBytes; list_data.back().down_kBytes += traffic.down_kBytes; } } for (const auto& data : list_data) { AddListRow(data, max_traffic); } } } void CHistoryTrafficListDlg::DoDataExchange(CDataExchange* pDX) { CTabDlg::DoDataExchange(pDX); DDX_Control(pDX, IDC_HISTORY_INFO_LIST, m_history_list); DDX_Control(pDX, IDC_VIEW_TYPE_COMBO, m_view_type_combo); DDX_Control(pDX, IDC_VIEW_SCALE_COMBO, m_view_scale_combo); } BEGIN_MESSAGE_MAP(CHistoryTrafficListDlg, CTabDlg) ON_WM_INITMENU() ON_COMMAND(ID_USE_LINEAR_SCALE, &CHistoryTrafficListDlg::OnUseLinearScale) ON_COMMAND(ID_USE_LOG_SCALE, &CHistoryTrafficListDlg::OnUseLogScale) ON_WM_SIZE() ON_CBN_SELCHANGE(IDC_VIEW_TYPE_COMBO, &CHistoryTrafficListDlg::OnCbnSelchangeViewTypeCombo) ON_CBN_SELCHANGE(IDC_VIEW_SCALE_COMBO, &CHistoryTrafficListDlg::OnCbnSelchangeViewScaleCombo) END_MESSAGE_MAP() // CHistoryTrafficListDlgDlg 消息处理程序 BOOL CHistoryTrafficListDlg::OnInitDialog() { CTabDlg::OnInitDialog(); // TODO: 在此添加额外的初始化 //初始化列表控件 m_history_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); std::vector widths; CalculateColumeWidth(widths); m_history_list.InsertColumn(0, CCommon::LoadText(IDS_DATE), LVCFMT_LEFT, widths[0]); m_history_list.InsertColumn(1, CCommon::LoadText(IDS_UPLOAD), LVCFMT_RIGHT, widths[1]); m_history_list.InsertColumn(2, CCommon::LoadText(IDS_DOWNLOAD), LVCFMT_RIGHT, widths[2]); m_history_list.InsertColumn(3, CCommon::LoadText(IDS_TRAFFIC_USED), LVCFMT_RIGHT, widths[3]); m_history_list.InsertColumn(4, CCommon::LoadText(IDS_FIGURE), LVCFMT_LEFT, widths[4]); m_history_list.EnableDrawItemRange(); m_history_list.SetDrawItemRangeRow(4); m_history_list.SetDrawItemRangMargin(theApp.DPI(4)); m_history_list.SetDrawItemRangInLogScale(theApp.m_cfg_data.m_use_log_scale); //初始化控件 m_view_type_combo.AddString(CCommon::LoadText(IDS_DAY_VIEW)); m_view_type_combo.AddString(CCommon::LoadText(IDS_WEEK_VIEW)); m_view_type_combo.AddString(CCommon::LoadText(IDS_MONTH_VIEW)); m_view_type_combo.AddString(CCommon::LoadText(IDS_QUARTER_VIEW)); m_view_type_combo.AddString(CCommon::LoadText(IDS_YEAR_VIEW)); m_view_type_combo.SetCurSel(static_cast(theApp.m_cfg_data.m_view_type)); m_view_scale_combo.AddString(CCommon::LoadText(IDS_LINEAR_SCALE)); m_view_scale_combo.AddString(CCommon::LoadText(IDS_LOG_SCALE)); m_view_scale_combo.SetCurSel(theApp.m_cfg_data.m_use_log_scale ? 1 : 0); //显示列表数据 ShowListData(); CCommon::LoadMenuResource(m_menu, IDR_HISTORY_TRAFFIC_MENU); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } BOOL CHistoryTrafficListDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) { // TODO: 在此添加专用代码和/或调用基类 LPNMHDR lpnmhdr = (LPNMHDR)lParam; if (lpnmhdr->code == NM_RCLICK) { CPoint point; GetCursorPos(&point);//获得光标的位置 m_history_list.ScreenToClient(&point);//获得list控件在窗口上的坐标 CWnd* pWnd = m_history_list.ChildWindowFromPoint(point); CHeaderCtrl* pHeader = m_history_list.GetHeaderCtrl();//获取列表视图控件的标题控件 CRect item_rect; pHeader->GetItemRect(4, item_rect); //获取列表标题控件第2列的矩形区域,只有光标在第4列点击时才弹出右键菜单 if (pWnd && (pWnd->GetSafeHwnd() == pHeader->GetSafeHwnd()) && item_rect.PtInRect(point)) { HDHITTESTINFO info{}; info.pt = point; pHeader->SendMessage(HDM_HITTEST, 0, (LPARAM)&info); CMenu * pMenu = m_menu.GetSubMenu(0); GetCursorPos(&point); pMenu->TrackPopupMenu(TPM_RIGHTBUTTON, point.x, point.y, this); } } return CTabDlg::OnNotify(wParam, lParam, pResult); } void CHistoryTrafficListDlg::OnInitMenu(CMenu* pMenu) { CTabDlg::OnInitMenu(pMenu); // TODO: 在此处添加消息处理程序代码 if (theApp.m_cfg_data.m_use_log_scale) pMenu->CheckMenuRadioItem(ID_USE_LINEAR_SCALE, ID_USE_LOG_SCALE, ID_USE_LOG_SCALE, MF_BYCOMMAND | MF_CHECKED); else pMenu->CheckMenuRadioItem(ID_USE_LINEAR_SCALE, ID_USE_LOG_SCALE, ID_USE_LINEAR_SCALE, MF_BYCOMMAND | MF_CHECKED); } void CHistoryTrafficListDlg::OnUseLinearScale() { // TODO: 在此添加命令处理程序代码 theApp.m_cfg_data.m_use_log_scale = false; m_history_list.SetDrawItemRangInLogScale(theApp.m_cfg_data.m_use_log_scale); } void CHistoryTrafficListDlg::OnUseLogScale() { // TODO: 在此添加命令处理程序代码 theApp.m_cfg_data.m_use_log_scale = true; m_history_list.SetDrawItemRangInLogScale(theApp.m_cfg_data.m_use_log_scale); } void CHistoryTrafficListDlg::OnSize(UINT nType, int cx, int cy) { CTabDlg::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 if (nType != SIZE_MINIMIZED) { std::vector widths; if(CalculateColumeWidth(widths)) { for (size_t i{}; i < widths.size(); i++) { m_history_list.SetColumnWidth(i, widths[i]); } } } } void CHistoryTrafficListDlg::OnCbnSelchangeViewTypeCombo() { // TODO: 在此添加控件通知处理程序代码 theApp.m_cfg_data.m_view_type = static_cast(m_view_type_combo.GetCurSel()); ShowListData(); } void CHistoryTrafficListDlg::OnCbnSelchangeViewScaleCombo() { // TODO: 在此添加控件通知处理程序代码 theApp.m_cfg_data.m_use_log_scale = (m_view_scale_combo.GetCurSel() != 0); m_history_list.SetDrawItemRangInLogScale(theApp.m_cfg_data.m_use_log_scale); } ================================================ FILE: TrafficMonitor/IDrawCommon.h ================================================ #pragma once #include "CommonData.h" class IDrawBuffer { public: virtual ~IDrawBuffer() = default; }; class IDrawCommon { public: // 拉伸模式 enum class StretchMode { STRETCH, // 拉伸,会改变比例 FILL, // 填充,不改变比例,会裁剪长边 FIT // 适应,不会改变比例,不裁剪 }; //对齐方式 enum class Alignment { LEFT, //左对齐 RIGHT, //右对齐 CENTER, //居中 }; virtual void SetBackColor(COLORREF back_color, BYTE alpha = 255) = 0; // 设置绘制文本的字体 virtual void SetFont(CFont* pfont) = 0; // 在指定的矩形区域内绘制文本 virtual void DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color, Alignment align = Alignment::LEFT, bool draw_back_ground = false, bool multi_line = false, BYTE alpha = 255) = 0; // 设置绘图剪辑区域 virtual void SetDrawRect(CRect rect) = 0; // 用纯色填充矩形 virtual void FillRect(CRect rect, COLORREF color, BYTE alpha = 255) = 0; // 绘制矩形边框。如果dot_line为true,则为虚线 virtual void DrawRectOutLine(CRect rect, COLORREF color, int width = 1, bool dot_line = false, BYTE alpha = 255) = 0; // 使用当前画笔画线 virtual void DrawLine(CPoint start_point, int height, COLORREF color, BYTE alpha = 255) = 0; virtual void SetTextColor(const COLORREF color, BYTE alpha = 255) = 0; // 绘制一个位图 // (注意:当stretch_mode设置为StretchMode::FILL(填充)时,会设置绘图剪辑区域,如果之后需要绘制其他图形, // 需要重新设置绘图剪辑区域,否则图片外的区域会无法绘制) virtual void DrawBitmap(HBITMAP hbitmap, CPoint start_point, CSize size, StretchMode stretch_mode = StretchMode::STRETCH, BYTE alpha = 255) = 0; virtual ~IDrawCommon() = default; //获取绘图上下文句柄。仅在GDI或GDI+时有效 virtual CDC* GetDC() { return nullptr; } //获取文本宽度 virtual int GetTextWidth(LPCTSTR lpszString) { return 0; } }; namespace DrawCommonHelper { /** * @brief 渲染器类型的枚举,新增渲染器类型时应当添加枚举到其中 * */ enum class RenderType { // 使用GDI DEFAULT, // 如果支持,使用D2D1 D2D1, // 如果系统大于win8.1,则在使用D2D1时利用DirectComposition呈现结果 D2D1_WITH_DCOMPOSITION }; constexpr BYTE GDI_NO_MODIFIED_FLAG = 0x01; constexpr BYTE OPAQUE_ALPHA_VALUE = 0xFF; constexpr BYTE TRANSPARENT_ALPHA_VALUE = 0x00; constexpr BYTE AVAILABLE_MINIMUM_ALPHA = 0x02; constexpr BYTE GDI_MODIFIED_FLAG = 0x00; } template class AlignedUnionStorage { private: alignas(Ts...) std::byte m_buffer[(std::max)({sizeof(Ts)...})]{}; public: AlignedUnionStorage() = default; ~AlignedUnionStorage() = default; std::byte* operator&() noexcept { return m_buffer; } }; ================================================ FILE: TrafficMonitor/IconSelectDlg.cpp ================================================ // IconSelectDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "IconSelectDlg.h" #include "afxdialogex.h" // CIconSelectDlg 对话框 IMPLEMENT_DYNAMIC(CIconSelectDlg, CBaseDialog) CIconSelectDlg::CIconSelectDlg(int icon_selected, CWnd* pParent /*=NULL*/) : CBaseDialog(IDD_ICON_SELECT_DIALOG, pParent), m_icon_selected{ icon_selected } { } CIconSelectDlg::~CIconSelectDlg() { } int CIconSelectDlg::GetIconSelected() const { if (m_icon_selected < 0 || m_icon_selected >= MAX_NOTIFY_ICON) return 0; return m_icon_selected; } void CIconSelectDlg::SetAutoAdaptNotifyIcon(bool val) { m_atuo_adapt_notify_icon = val; } bool CIconSelectDlg::AutoAdaptNotifyIcon() const { return m_atuo_adapt_notify_icon; } void CIconSelectDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_ICON_PREVIEW, m_preview_pic); DDX_Control(pDX, IDC_COMBO1, m_icon_select_combo); DDX_Control(pDX, IDC_AUTO_ADAPT_CHECK, m_auto_adapt_chk); } void CIconSelectDlg::DrawPreviewIcon(CDC* pDC) { //pDC->FillSolidRect(CRect(CPoint(ICON_X, ICON_Y), CSize(theApp.DPI(16), theApp.DPI(16))), RGB(0, 0, 0)); //pDC->DrawIcon(ICON_X, ICON_Y, m_icons[m_icon_selected]); ::DrawIconEx(pDC->m_hDC, ICON_X, ICON_Y, theApp.m_notify_icons[GetIconSelected()], theApp.DPI(16), theApp.DPI(16), 0, NULL, DI_NORMAL); } BEGIN_MESSAGE_MAP(CIconSelectDlg, CBaseDialog) //ON_WM_TIMER() ON_CBN_SELCHANGE(IDC_COMBO1, &CIconSelectDlg::OnCbnSelchangeCombo1) ON_MESSAGE(WM_CONTROL_REPAINT, &CIconSelectDlg::OnControlRepaint) ON_BN_CLICKED(IDC_AUTO_ADAPT_CHECK, &CIconSelectDlg::OnBnClickedAutoAdaptCheck) END_MESSAGE_MAP() // CIconSelectDlg 消息处理程序 BOOL CIconSelectDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_NOTIFY), FALSE); // 设置小图标 //设置预览图大小 m_preview_pic.SetWindowPos(nullptr, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, SWP_NOZORDER | SWP_NOMOVE); if (m_icon_selected == 4 || m_icon_selected == 5) m_preview_pic.SetPicture((HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_NOTIFY_ICON_PREVIEW_LIGHT), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_CREATEDIBSECTION)); else m_preview_pic.SetPicture((HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_NOTIFY_ICON_PREVIEW), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_CREATEDIBSECTION)); //初始化下拉列表 m_icon_select_combo.AddString(CCommon::LoadText(IDS_DEFAULT_ICON)); m_icon_select_combo.AddString(CCommon::LoadText(IDS_ICON, _T(" 1"))); m_icon_select_combo.AddString(CCommon::LoadText(IDS_ICON, _T(" 2"))); m_icon_select_combo.AddString(CCommon::LoadText(IDS_ICON, _T(" 3"))); m_icon_select_combo.AddString(CCommon::LoadText(IDS_ICON, _T(" 4"))); m_icon_select_combo.AddString(CCommon::LoadText(IDS_ICON, _T(" 5"))); m_icon_select_combo.SetCurSel(m_icon_selected); m_auto_adapt_chk.SetCheck(m_atuo_adapt_notify_icon); m_auto_adapt_chk.EnableWindow(theApp.m_win_version.GetMajorVersion() >= 10); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CIconSelectDlg::OnCbnSelchangeCombo1() { // TODO: 在此添加控件通知处理程序代码 m_icon_selected = m_icon_select_combo.GetCurSel(); if (m_icon_selected == 4 || m_icon_selected == 5) m_preview_pic.SetPicture((HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_NOTIFY_ICON_PREVIEW_LIGHT), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_CREATEDIBSECTION)); else m_preview_pic.SetPicture((HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_NOTIFY_ICON_PREVIEW), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_CREATEDIBSECTION)); DrawPreviewIcon(m_preview_pic.GetDC()); } afx_msg LRESULT CIconSelectDlg::OnControlRepaint(WPARAM wParam, LPARAM lParam) { CWnd* pControl = (CWnd*)wParam; CDC* pDC = (CDC*)lParam; if (pControl == &m_preview_pic) { //当收到m_preview_pic控件的重绘消息时,同时重绘图标 DrawPreviewIcon(pDC); } return 0; } void CIconSelectDlg::OnBnClickedAutoAdaptCheck() { // TODO: 在此添加控件通知处理程序代码 m_atuo_adapt_notify_icon = (m_auto_adapt_chk.GetCheck() != 0); } CString CIconSelectDlg::GetDialogName() const { return _T("IconSelectDlg"); } bool CIconSelectDlg::InitializeControls() { RepositionTextBasedControls({ { CtrlTextInfo::L1, IDC_SELECT_ICON_STATIC }, { CtrlTextInfo::C0, IDC_COMBO1 } }); return true; } ================================================ FILE: TrafficMonitor/IconSelectDlg.h ================================================ #pragma once #include "afxwin.h" #include "PictureStatic.h" #include "BaseDialog.h" // CIconSelectDlg 对话框 class CIconSelectDlg : public CBaseDialog { DECLARE_DYNAMIC(CIconSelectDlg) public: CIconSelectDlg(int icon_selected, CWnd* pParent = NULL); // 标准构造函数 virtual ~CIconSelectDlg(); int GetIconSelected() const; void SetAutoAdaptNotifyIcon(bool val); bool AutoAdaptNotifyIcon() const; // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ICON_SELECT_DIALOG }; #endif #define PREVIEW_WIDTH theApp.DPI(200) //预览图的宽高 #define PREVIEW_HEIGHT theApp.DPI(40) #define ICON_X theApp.DPI(46) //预览图中图标的位置 #define ICON_Y theApp.DPI(12) protected: CPictureStatic m_preview_pic; CComboBox m_icon_select_combo; CButton m_auto_adapt_chk; int m_icon_selected{}; bool m_atuo_adapt_notify_icon; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 virtual CString GetDialogName() const override; virtual bool InitializeControls() override; void DrawPreviewIcon(CDC* pDC); DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); //afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnCbnSelchangeCombo1(); protected: afx_msg LRESULT OnControlRepaint(WPARAM wParam, LPARAM lParam); public: afx_msg void OnBnClickedAutoAdaptCheck(); }; ================================================ FILE: TrafficMonitor/Image2DEffect.cpp ================================================ #include "stdafx.h" #include "Image2DEffect.h" #include "Common.h" namespace D3DQuadrangle { const std::array VERTEX_INDEX_LIST{ 0, 1, 2, 2, 3, 0}; const CShader& GetVsShader() { static auto result = MakeStaticVariableWrapper( [](CShader* p_content) { p_content->SetCode( CIMAGE2DEFFECT_SHADER_VS_INPUT_DECLARATION CIMAGE2DEFFECT_SHADER_VS_OUTPUT_DECLARATION R"( VsOutput VS(VsInput input){ VsOutput result; result.position = input.position; result.texcoord = input.texcoord; return result; } )") .SetEntryPoint("VS") .SetName("D3DQuadrangleDefaultVS") .SetTarget("vs_4_1") .SetFlags1(D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_WARNINGS_ARE_ERRORS); }); return result.Get(); } } CImage2DEffect::CImage2DEffect(Microsoft::WRL::ComPtr p_device1) : m_p_owner_device1{p_device1} { RecreateUnmodifiableResource(m_p_owner_device1); RecreateVsByteCodeRelatedResources(m_p_vs_byte_code); } void CImage2DEffect::RecreateUnmodifiableResource(Microsoft::WRL::ComPtr p_device1) { constexpr static std::array input_elements_desc{ {{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, SLOT, offsetof(Image2DVertex, position), D3D10_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, SLOT, offsetof(Image2DVertex, texture0), D3D10_INPUT_PER_VERTEX_DATA, 0}}}; ThrowIfFailed( p_device1->CreateInputLayout( input_elements_desc.data(), static_cast(input_elements_desc.size()), m_p_vs_byte_code->GetBufferPointer(), m_p_vs_byte_code->GetBufferSize(), &m_p_input_layout), TRAFFICMONITOR_ERROR_STR("CImage2DEffect call CreateInputLayout failed.")); QuadrangleVertexs vertexes; float* vertex_position; float* vertex_texture; // 左上角 vertex_position = vertexes.GetLeftTopVertex().position; vertex_position[0] = -1.f; // x vertex_position[1] = 1.f; // y vertex_position[2] = .0f; // z vertex_texture = vertexes.GetLeftTopVertex().texture0; vertex_texture[0] = 0.f; // u vertex_texture[1] = 0.f; // v // 右上角 vertex_position = vertexes.GetRightTopVertex().position; vertex_position[0] = 1.f; vertex_position[1] = 1.f; vertex_position[2] = .0f; vertex_texture = vertexes.GetRightTopVertex().texture0; vertex_texture[0] = 1.f; vertex_texture[1] = 0.f; // 右下角 vertex_position = vertexes.GetRightBottomVertex().position; vertex_position[0] = 1.f; vertex_position[1] = -1.f; vertex_position[2] = .0f; vertex_texture = vertexes.GetRightBottomVertex().texture0; vertex_texture[0] = 1.f; vertex_texture[1] = 1.f; // 左下角 vertex_position = vertexes.GetLeftBottomVertex().position; vertex_position[0] = -1.f; vertex_position[1] = -1.f; vertex_position[2] = .0f; vertex_texture = vertexes.GetLeftBottomVertex().texture0; vertex_texture[0] = 0; vertex_texture[1] = 1; D3D10_BUFFER_DESC vertex_buffer_desc{}; vertex_buffer_desc.ByteWidth = static_cast(vertexes.GetSize()); vertex_buffer_desc.Usage = D3D10_USAGE_IMMUTABLE; vertex_buffer_desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; D3D10_SUBRESOURCE_DATA vertex_data{}; vertex_data.pSysMem = vertexes.GetData(); ThrowIfFailed( p_device1->CreateBuffer( &vertex_buffer_desc, &vertex_data, &m_p_vertex_buffer), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create vertex buffer failed.")); D3D10_BUFFER_DESC index_buffer_desc{}; index_buffer_desc.Usage = D3D10_USAGE_IMMUTABLE; index_buffer_desc.ByteWidth = sizeof(D3DQuadrangle::VERTEX_INDEX_LIST); index_buffer_desc.BindFlags = D3D10_BIND_INDEX_BUFFER; D3D10_SUBRESOURCE_DATA index_data{}; index_data.pSysMem = &D3DQuadrangle::VERTEX_INDEX_LIST; ThrowIfFailed( p_device1->CreateBuffer( &index_buffer_desc, &index_data, &m_p_index_buffer), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create index buffer failed.")); D3D10_RASTERIZER_DESC rasterizer_desc{}; rasterizer_desc.FillMode = D3D10_FILL_SOLID; rasterizer_desc.CullMode = D3D10_CULL_NONE; rasterizer_desc.FrontCounterClockwise = FALSE; rasterizer_desc.DepthBias = 0; rasterizer_desc.DepthBiasClamp = 0.0f; rasterizer_desc.SlopeScaledDepthBias = 0.0f; rasterizer_desc.DepthClipEnable = FALSE; rasterizer_desc.ScissorEnable = FALSE; rasterizer_desc.MultisampleEnable = FALSE; rasterizer_desc.AntialiasedLineEnable = FALSE; ThrowIfFailed( p_device1->CreateRasterizerState( &rasterizer_desc, &m_p_rasterizer_state), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create rasterizer state failed.")); D3D10_SAMPLER_DESC tex0_sampler = {}; tex0_sampler.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; tex0_sampler.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP; tex0_sampler.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP; tex0_sampler.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP; tex0_sampler.MipLODBias = 0.0f; tex0_sampler.MaxAnisotropy = 1; tex0_sampler.ComparisonFunc = D3D10_COMPARISON_NEVER; tex0_sampler.MinLOD = -FLT_MAX; tex0_sampler.MaxLOD = FLT_MAX; ThrowIfFailed( p_device1->CreateSamplerState( &tex0_sampler, &m_p_ps_tex0_sampler_state), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create default sampler state failed.")); D3D10_BLEND_DESC1 blend_desc1{}; blend_desc1.AlphaToCoverageEnable = FALSE; blend_desc1.IndependentBlendEnable = FALSE; auto& render_target_blend_desc0 = blend_desc1.RenderTarget[0]; render_target_blend_desc0.BlendEnable = TRUE; render_target_blend_desc0.SrcBlend = D3D10_BLEND_SRC_ALPHA; render_target_blend_desc0.DestBlend = D3D10_BLEND_INV_DEST_ALPHA; render_target_blend_desc0.BlendOp = D3D10_BLEND_OP_ADD; render_target_blend_desc0.SrcBlendAlpha = D3D10_BLEND_ONE; render_target_blend_desc0.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; render_target_blend_desc0.BlendOpAlpha = D3D10_BLEND_OP_ADD; render_target_blend_desc0.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; ThrowIfFailed( p_device1->CreateBlendState1( &blend_desc1, &m_p_blend_state1), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create D3D10.1 blend state for alpha blend failed.")); D3D10_DEPTH_STENCIL_DESC depth_stencil_desc{}; depth_stencil_desc.DepthEnable = FALSE; depth_stencil_desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; depth_stencil_desc.DepthFunc = D3D10_COMPARISON_LESS; depth_stencil_desc.StencilEnable = FALSE; depth_stencil_desc.StencilReadMask = 0xFF; depth_stencil_desc.StencilWriteMask = 0xFF; depth_stencil_desc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; depth_stencil_desc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR; depth_stencil_desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; depth_stencil_desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS; depth_stencil_desc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; depth_stencil_desc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR; depth_stencil_desc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; depth_stencil_desc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS; ThrowIfFailed( p_device1->CreateDepthStencilState( &depth_stencil_desc, &m_p_depth_stencil_state), TRAFFICMONITOR_ERROR_STR("CImage2DEffect call CreateDepthStencilState failed.")); } void CImage2DEffect::RecreateInputTextureRelatedResources(Microsoft::WRL::ComPtr p_input) { D3D10_SHADER_RESOURCE_VIEW_DESC tex0_shader_resource_view_desc = {}; tex0_shader_resource_view_desc.Format = PIXEL_FORMAT; tex0_shader_resource_view_desc.ViewDimension = D3D10_1_SRV_DIMENSION_TEXTURE2D; tex0_shader_resource_view_desc.Texture2D.MostDetailedMip = 0; tex0_shader_resource_view_desc.Texture2D.MipLevels = 1; ThrowIfFailed( m_p_owner_device1->CreateShaderResourceView( p_input.Get(), &tex0_shader_resource_view_desc, &m_p_ps_tex0_resource_view), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create shader resource view1 failed.")); } void CImage2DEffect::RecreateOutputTextureRelatedResources(Microsoft::WRL::ComPtr p_output) { ThrowIfFailed( m_p_owner_device1->CreateRenderTargetView( p_output.Get(), NULL, &m_p_render_target_view), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create D3D10.1 render target view failed.")); } void CImage2DEffect::RecreateVsByteCodeRelatedResources(Microsoft::WRL::ComPtr p_vs_byte_code) { ThrowIfFailed( m_p_owner_device1->CreateVertexShader( p_vs_byte_code->GetBufferPointer(), p_vs_byte_code->GetBufferSize(), &m_p_vs), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create vertex shader failed.")); } void CImage2DEffect::RecreatePsByteCodeRelatedResources(Microsoft::WRL::ComPtr p_ps_byte_code) { ThrowIfFailed( m_p_owner_device1->CreatePixelShader( p_ps_byte_code->GetBufferPointer(), p_ps_byte_code->GetBufferSize(), &m_p_ps), TRAFFICMONITOR_ERROR_STR("CImage2DEffect create pixel shader failed.")); } auto CImage2DEffect::ApplyPipelineConfig() const -> const CImage2DEffect& { m_p_owner_device1->ClearState(); m_p_owner_device1->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_p_owner_device1->IASetInputLayout(m_p_input_layout.Get()); auto raw_m_p_vertex_buffer = m_p_vertex_buffer.Get(); constexpr static std::array strides{static_cast(QuadrangleVertexs::GetStride())}; constexpr static std::array offsets{0}; m_p_owner_device1->IASetVertexBuffers( SLOT, 1, &raw_m_p_vertex_buffer, strides.data(), offsets.data()); m_p_owner_device1->IASetIndexBuffer( m_p_index_buffer.Get(), DXGI_FORMAT_R16_UINT, 0); m_p_owner_device1->VSSetShader(m_p_vs.Get()); D3D10_VIEWPORT viewport{}; viewport.Width = m_output_width; viewport.Height = m_output_height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; m_p_owner_device1->RSSetViewports(1, &viewport); m_p_owner_device1->RSSetState(m_p_rasterizer_state.Get()); ID3D10SamplerState* raw_m_p_ps_tex0_sampler_state = m_p_ps_tex0_sampler_state.Get(); m_p_owner_device1->PSSetSamplers( SLOT, 1, &raw_m_p_ps_tex0_sampler_state); auto raw_m_p_ps_tex0_resource_view = m_p_ps_tex0_resource_view.Get(); m_p_owner_device1->PSSetShaderResources( SLOT, 1, &raw_m_p_ps_tex0_resource_view); m_p_owner_device1->PSSetShader(m_p_ps.Get()); m_p_owner_device1->OMSetBlendState(m_p_blend_state1.Get(), NULL, 0xffffffff); m_p_owner_device1->OMSetDepthStencilState(m_p_depth_stencil_state.Get(), 0); auto p_render_target_view = m_p_render_target_view.Get(); m_p_owner_device1->OMSetRenderTargets(1, &p_render_target_view, NULL); return *this; } auto CImage2DEffect::RebindDevice1(Microsoft::WRL::ComPtr p_device1) -> CImage2DEffect& { RecreateUnmodifiableResource(p_device1); m_p_owner_device1 = p_device1; m_p_input.Reset(); m_p_output.Reset(); if (m_p_vs_byte_code) { RecreateVsByteCodeRelatedResources(m_p_vs_byte_code); } if (m_p_ps_byte_code) { RecreatePsByteCodeRelatedResources(m_p_ps_byte_code); } return *this; } auto CImage2DEffect::SetOutputSize(UINT width, UINT height) noexcept -> CImage2DEffect& { m_output_width = width > 0 ? width : 1; m_output_height = height > 0 ? height : 1; return *this; } UINT CImage2DEffect::GetOutputWidth() const noexcept { return m_output_width; } UINT CImage2DEffect::GetOutputHeight() const noexcept { return m_output_height; } auto CImage2DEffect::SetInputTexture(Microsoft::WRL::ComPtr p_input) -> CImage2DEffect& { if(m_p_input == p_input) { return *this; } RecreateInputTextureRelatedResources(p_input); m_p_input = p_input; return *this; } auto CImage2DEffect::GetInputTexture() noexcept -> Microsoft::WRL::ComPtr { return m_p_input; } auto CImage2DEffect::SetOutputTexture(Microsoft::WRL::ComPtr p_output) -> CImage2DEffect& { if (m_p_output == p_output) { return *this; } RecreateOutputTextureRelatedResources(p_output); m_p_output = p_output; return *this; } auto CImage2DEffect::GetOutputTexture() noexcept -> Microsoft::WRL::ComPtr { return m_p_output; } auto CImage2DEffect::SetVsByteCode(Microsoft::WRL::ComPtr p_vs_byte_code) -> CImage2DEffect& { RecreateVsByteCodeRelatedResources(p_vs_byte_code); m_p_vs_byte_code = p_vs_byte_code; return *this; } auto CImage2DEffect::GetVsByteCode() noexcept -> Microsoft::WRL::ComPtr { return m_p_vs_byte_code; } auto CImage2DEffect::SetPsByteCode(Microsoft::WRL::ComPtr p_ps_byte_code) -> CImage2DEffect& { RecreatePsByteCodeRelatedResources(p_ps_byte_code); m_p_ps_byte_code = p_ps_byte_code; return *this; } auto CImage2DEffect::GetPsByteCode() noexcept -> Microsoft::WRL::ComPtr { return m_p_ps_byte_code; } auto CImage2DEffect::Draw() const -> CD3D10DrawCallWaiter { DrawOnly(); return CD3D10DrawCallWaiter{m_p_owner_device1}; } void CImage2DEffect::DrawOnly() const noexcept { constexpr auto index_count = static_cast(D3DQuadrangle::VERTEX_INDEX_LIST.size()); m_p_owner_device1->DrawIndexed(index_count, 0, 0); } auto CImage2DEffect::Clear(const std::array& color) const -> CD3D10DrawCallWaiter { ClearOnly(color); return CD3D10DrawCallWaiter{m_p_owner_device1}; } auto CImage2DEffect::ClearOnly(const std::array& color) const noexcept -> const CImage2DEffect& { m_p_owner_device1->ClearRenderTargetView(m_p_render_target_view.Get(), color.data()); return *this; } ================================================ FILE: TrafficMonitor/Image2DEffect.h ================================================ #pragma once #include #include #include "D3D10Support1.h" namespace D3DQuadrangle { extern const std::array VERTEX_INDEX_LIST; const CShader& GetVsShader(); /** * @brief 由三角形012和三角形132组成的四边形,顶点序号见VERTEX_INDEX_LIST * * @tparam Vertex 顶点类型 */ template struct QuadrangleVertexs { /** * @brief 由三角形012和三角形123组成的四边形,下面是顶点排列顺序: \n * 1·--------------·2 \n * | | \n * | | \n * 0·--------------·3 */ std::array vertexs{}; Vertex& GetLeftTopVertex() noexcept { return vertexs[1]; } Vertex& GetRightTopVertex() noexcept { return vertexs[2]; } Vertex& GetLeftBottomVertex() noexcept { return vertexs[0]; } Vertex& GetRightBottomVertex() noexcept { return vertexs[3]; } Vertex* GetData() noexcept { return vertexs.data(); } static constexpr std::size_t GetSize() noexcept { return sizeof(vertexs); } static constexpr std::size_t GetStride() noexcept { return sizeof(Vertex); } }; } struct Image2DVertex { float position[4]; float texture0[2]; }; /** * @brief 默认vertex shader的输入输出定义 \n struct VsInput{ \n float4 position : POSITION; \n float2 texcoord : TEXCOORD; \n }; \n \n struct VsOutput{ \n float4 position : SV_POSITION; \n float2 texcoord : TEXCOORD; \n }; * */ class CImage2DEffect { private: Microsoft::WRL::ComPtr m_p_owner_device1{}; Microsoft::WRL::ComPtr m_p_vs_byte_code{D3DQuadrangle::GetVsShader().Compile()}; Microsoft::WRL::ComPtr m_p_ps_byte_code{}; Microsoft::WRL::ComPtr m_p_input{}; Microsoft::WRL::ComPtr m_p_output{}; //内部使用 Microsoft::WRL::ComPtr m_p_input_layout{}; Microsoft::WRL::ComPtr m_p_vertex_buffer{}; Microsoft::WRL::ComPtr m_p_index_buffer{}; Microsoft::WRL::ComPtr m_p_vs{}; Microsoft::WRL::ComPtr m_p_rasterizer_state{}; Microsoft::WRL::ComPtr m_p_blend_state1{}; Microsoft::WRL::ComPtr m_p_depth_stencil_state{}; UINT m_output_width{}; UINT m_output_height{}; Microsoft::WRL::ComPtr m_p_ps_tex0_resource_view{}; Microsoft::WRL::ComPtr m_p_ps_tex0_sampler_state{}; Microsoft::WRL::ComPtr m_p_ps{}; Microsoft::WRL::ComPtr m_p_render_target_view{}; protected: void RecreateUnmodifiableResource(Microsoft::WRL::ComPtr p_device1); void RecreateInputTextureRelatedResources(Microsoft::WRL::ComPtr p_input); void RecreateOutputTextureRelatedResources(Microsoft::WRL::ComPtr p_output); void RecreateVsByteCodeRelatedResources(Microsoft::WRL::ComPtr p_vs_byte_code); void RecreatePsByteCodeRelatedResources(Microsoft::WRL::ComPtr p_ps_byte_code); public: constexpr static auto PIXEL_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; constexpr static UINT SLOT = 0; using QuadrangleVertexs = D3DQuadrangle::QuadrangleVertexs; CImage2DEffect(Microsoft::WRL::ComPtr p_device1); ~CImage2DEffect() = default; auto RebindDevice1(Microsoft::WRL::ComPtr p_device1) -> CImage2DEffect&; auto SetOutputSize(UINT width, UINT height) noexcept -> CImage2DEffect&; UINT GetOutputWidth() const noexcept; UINT GetOutputHeight() const noexcept; auto SetInputTexture(Microsoft::WRL::ComPtr p_input) -> CImage2DEffect&; auto GetInputTexture() noexcept -> Microsoft::WRL::ComPtr; auto SetOutputTexture(Microsoft::WRL::ComPtr p_output) -> CImage2DEffect&; auto GetOutputTexture() noexcept -> Microsoft::WRL::ComPtr; auto SetVsByteCode(Microsoft::WRL::ComPtr p_vs_byte_code) -> CImage2DEffect&; auto GetVsByteCode() noexcept -> Microsoft::WRL::ComPtr; auto SetPsByteCode(Microsoft::WRL::ComPtr p_ps_byte_code) -> CImage2DEffect&; auto GetPsByteCode() noexcept -> Microsoft::WRL::ComPtr; auto ApplyPipelineConfig() const -> const CImage2DEffect&; [[nodiscard]] auto Draw() const -> CD3D10DrawCallWaiter; void DrawOnly() const noexcept; [[nodiscard]] auto Clear(const std::array& color) const -> CD3D10DrawCallWaiter; auto ClearOnly(const std::array& color) const noexcept -> const CImage2DEffect&; }; #define CIMAGE2DEFFECT_SHADER_VS_INPUT_DECLARATION \ "struct VsInput" \ "{" \ " float4 position : POSITION;" \ " float2 texcoord : TEXCOORD;" \ "};" #define CIMAGE2DEFFECT_SHADER_VS_OUTPUT_DECLARATION \ "struct VsOutput" \ "{" \ " float4 position : SV_POSITION;" \ " float2 texcoord : TEXCOORD;" \ "};" ================================================ FILE: TrafficMonitor/IniHelper.cpp ================================================ #include "stdafx.h" #include "IniHelper.h" #include "Common.h" CIniHelper::CIniHelper(const wstring& file_path, bool force_utf8) { m_file_path = file_path; ifstream file_stream{ file_path }; if (!file_stream.is_open()) return; // 获取文件大小 file_stream.seekg(0, std::ios::end); size_t file_size = static_cast(file_stream.tellg()); file_stream.seekg(0, std::ios::beg); // 读取文件内容 string ini_str; ini_str.resize(file_size + 1); file_stream.read(&ini_str[0], file_size); // 检查并添加末尾的空行 if (!ini_str.empty() && ini_str.back() != L'\n') ini_str.push_back(L'\n'); bool is_utf8; if (force_utf8) { is_utf8 = true; } else { //判断文件是否是utf8编码 if (ini_str.size() >= 3 && ini_str[0] == -17 && ini_str[1] == -69 && ini_str[2] == -65) { //如果有UTF8的BOM,则删除BOM is_utf8 = true; ini_str = ini_str.substr(3); } else { is_utf8 = false; } } //转换成Unicode m_ini_str = CCommon::StrToUnicode(ini_str.c_str(), is_utf8); } CIniHelper::CIniHelper(UINT id, bool is_utf8) { m_ini_str = CCommon::GetTextResource(id, is_utf8 ? 1 : 0); } CIniHelper::CIniHelper() { } CIniHelper::~CIniHelper() { } void CIniHelper::FromDirectString(const wstring& str_content) { m_ini_str = str_content; } void CIniHelper::SetSaveAsUTF8(bool utf8) { m_save_as_utf8 = utf8; } void CIniHelper::WriteString(const wchar_t * AppName, const wchar_t * KeyName, const wstring& str) { wstring write_str{ str }; if (!write_str.empty() && (write_str[0] == L' ' || write_str.back() == L' ')) //如果字符串前后含有空格,则在字符串前后添加引号 { write_str = DEF_CH + write_str; write_str.push_back(DEF_CH); } _WriteString(AppName, KeyName, write_str); } wstring CIniHelper::GetString(const wchar_t * AppName, const wchar_t * KeyName, const wchar_t* default_str) const { wstring rtn{ default_str }; GetString(AppName, KeyName, rtn); return rtn; } bool CIniHelper::GetString(const wchar_t* AppName, const wchar_t* KeyName, wstring& str) const { bool rtn = _GetString(AppName, KeyName, str); //如果读取的字符串前后有指定的字符,则删除它 if (!str.empty() && (str.front() == L'$' || str.front() == DEF_CH)) str = str.substr(1); if (!str.empty() && (str.back() == L'$' || str.back() == DEF_CH)) str.pop_back(); return rtn; } void CIniHelper::WriteInt(const wchar_t * AppName, const wchar_t * KeyName, int value) { _WriteString(AppName, KeyName, std::to_wstring(value)); } int CIniHelper::GetInt(const wchar_t * AppName, const wchar_t * KeyName, int default_value) const { wstring rtn{ std::to_wstring(default_value) }; _GetString(AppName, KeyName, rtn); return _ttoi(rtn.c_str()); } void CIniHelper::WriteBool(const wchar_t * AppName, const wchar_t * KeyName, bool value) { if(value) _WriteString(AppName, KeyName, wstring(L"true")); else _WriteString(AppName, KeyName, wstring(L"false")); } bool CIniHelper::GetBool(const wchar_t * AppName, const wchar_t * KeyName, bool default_value) const { wstring rtn{ default_value ? L"true" : L"false" }; _GetString(AppName, KeyName, rtn); if (rtn == L"true") return true; else if (rtn == L"false") return false; else return (_ttoi(rtn.c_str()) != 0); } void CIniHelper::WriteIntArray(const wchar_t * AppName, const wchar_t * KeyName, const int * values, int size) { CString str, tmp; for (int i{}; i < size; i++) { tmp.Format(_T("%d,"), values[i]); str += tmp; } _WriteString(AppName, KeyName, wstring(str)); } void CIniHelper::GetIntArray(const wchar_t * AppName, const wchar_t * KeyName, int * values, int size, int default_value) const { CString default_str; default_str.Format(_T("%d"), default_value); wstring str{ default_str.GetString() }; _GetString(AppName, KeyName, str); std::vector split_result; CCommon::StringSplit(str, L',', split_result); for (int i = 0; i < size; i++) { if (i < split_result.size()) values[i] = _wtoi(split_result[i].c_str()); else if (i > 0) values[i] = values[i - 1]; else values[i] = default_value; } } void CIniHelper::WriteBoolArray(const wchar_t * AppName, const wchar_t * KeyName, const bool * values, int size) { int value{}; for (int i{}; i < size; i++) { if (values[i]) value |= (1 << i); } return WriteInt(AppName, KeyName, value); } void CIniHelper::GetBoolArray(const wchar_t * AppName, const wchar_t * KeyName, bool * values, int size, bool default_value) const { int value = GetInt(AppName, KeyName, 0); for (int i{}; i < size; i++) { values[i] = ((value >> i) % 2 != 0); } } void CIniHelper::WriteStringList(const wchar_t* AppName, const wchar_t* KeyName, const vector& values) { wstring str_write = MergeStringList(values); _WriteString(AppName, KeyName, str_write); } void CIniHelper::GetStringList(const wchar_t* AppName, const wchar_t* KeyName, vector& values, const vector& default_value) const { wstring str_value = MergeStringList(default_value); _GetString(AppName, KeyName, str_value); SplitStringList(values, str_value); } vector CIniHelper::GetAllAppName(const wstring& prefix) const { vector list; size_t pos{}; while ((pos = m_ini_str.find(L"\n[" + prefix, pos)) != wstring::npos) { size_t end = m_ini_str.find(L']', pos + 1); if (end != wstring::npos) { wstring tmp(m_ini_str.begin() + pos + prefix.size() + 2, m_ini_str.begin() + end); list.push_back(std::move(tmp)); pos = end + 1; } } return list; } void CIniHelper::GetAllKeyValues(const wstring& AppName, std::map& map) const { wstring app_str{ L"[" }; app_str.append(AppName).append(L"]"); size_t app_pos{}, app_end_pos{}; app_pos = m_ini_str.find(app_str); if (app_pos == wstring::npos) return; app_end_pos = m_ini_str.find(L"\n[", app_pos + 2); if (app_end_pos != wstring::npos) app_end_pos++; app_str = m_ini_str.substr(app_pos, app_end_pos - app_pos); vector line; CCommon::StringSplit(app_str, L'\n', line); for (wstring str : line) { // CCommon::StringSplit会跳过空字符串,str一定非空 if (str[0] == L';' || str[0] == L'#') // 跳过注释行(只支持行首注释) continue; size_t pos = str.find_first_of(L'='); if (pos == wstring::npos) continue; wstring key{ str.substr(0, pos) }; wstring value{ str.substr(pos + 1) }; CCommon::StringNormalize(key); CCommon::StringNormalize(value); if (!key.empty() && !value.empty()) { if (value.front() == L'\"' && value.back() == L'\"') value = value.substr(1, value.size() - 2); UnEscapeString(value); map[key] = value; } } } bool CIniHelper::RemoveSection(const wstring& AppName) { if (AppName.empty()) return false; wstring app_str{ L"[" }; app_str.append(AppName).append(L"]"); size_t app_pos{}, app_end_pos{}; app_pos = m_ini_str.find(app_str); if (app_pos == wstring::npos) //找不到AppName,返回默认字符串 return false; app_end_pos = m_ini_str.find(L"\n[", app_pos + 2); if (app_end_pos != wstring::npos) app_end_pos++; m_ini_str.erase(app_pos, app_end_pos - app_pos); return true; } bool CIniHelper::Save() { if (m_file_path.empty()) return false; ofstream file_stream{ m_file_path }; if(file_stream.fail()) return false; string ini_str{ CCommon::UnicodeToStr(m_ini_str.c_str(), m_save_as_utf8) }; if (m_save_as_utf8) //如果以UTF8编码保存,先插入BOM { string utf8_bom; utf8_bom.push_back(-17); utf8_bom.push_back(-69); utf8_bom.push_back(-65); file_stream << utf8_bom; } file_stream << ini_str; return true; } void CIniHelper::UnEscapeString(wstring& str) { bool escape{ false }; wstring result; result.reserve(str.size()); for (size_t i = 0; i < str.size(); i++) { wchar_t ch = str[i]; if (escape) { switch (ch) { case L'n': result += L'\n'; break; case L'r': result += L'\r'; break; case L't': result += L'\t'; break; case L'"': result += L'"'; break; case L';': result += L';'; break; case L'#': result += L'#'; break; case L'\\': result += L'\\'; break; default:result += '\\'; result += ch; break; } escape = false; } else if (ch == L'\\') escape = true; else if (i > 0 && ch == '\"' && str[i - 1] == '\"') //两个连续的引号只保留一个引号 continue; else result += ch; } str.swap(result); } void CIniHelper::_WriteString(const wchar_t * AppName, const wchar_t * KeyName, const wstring & str) { wstring app_str{ L"[" }; app_str.append(AppName).append(L"]"); size_t app_pos{}, app_end_pos, key_pos; app_pos = m_ini_str.find(app_str); if (app_pos == wstring::npos) //找不到AppName,则在最后面添加 { if (!m_ini_str.empty() && m_ini_str.back() != L'\n') m_ini_str += L"\n"; app_pos = m_ini_str.size(); m_ini_str += app_str; m_ini_str += L"\n"; } app_end_pos = m_ini_str.find(L"\n[", app_pos + 2); if (app_end_pos != wstring::npos) app_end_pos++; key_pos = m_ini_str.find(wstring(L"\n") + KeyName + L' ', app_pos); //查找“\nkey_name ” if (key_pos >= app_end_pos) //如果找不到“\nkey_name ”,则查找“\nkey_name=” key_pos = m_ini_str.find(wstring(L"\n") + KeyName + L'=', app_pos); if (key_pos >= app_end_pos) //找不到KeyName,则插入一个 { //wchar_t buff[256]; //swprintf_s(buff, L"%s = %s\n", KeyName, str.c_str()); std::wstring str_temp = KeyName; str_temp += L" = "; str_temp += str; str_temp += L"\n"; if (app_end_pos == wstring::npos) m_ini_str += str_temp; else m_ini_str.insert(app_end_pos, str_temp); } else //找到了KeyName,将等号到换行符之间的文本替换 { size_t str_pos; str_pos = m_ini_str.find(L'=', key_pos + 2); size_t line_end_pos = m_ini_str.find(L'\n', key_pos + 2); if (str_pos > line_end_pos) //所在行没有等号,则插入一个等号 { m_ini_str.insert(key_pos + wcslen(KeyName) + 1, L" ="); str_pos = key_pos + wcslen(KeyName) + 2; } else { str_pos++; } size_t str_end_pos; str_end_pos = m_ini_str.find(L"\n", str_pos); m_ini_str.replace(str_pos, str_end_pos - str_pos, L" " + str); } } bool CIniHelper::_GetString(const wchar_t* AppName, const wchar_t* KeyName, wstring& str) const { wstring app_str{ L"[" }; app_str.append(AppName).append(L"]"); size_t app_pos{}, app_end_pos, key_pos; app_pos = m_ini_str.find(app_str); if (app_pos == wstring::npos) //找不到AppName,返回默认字符串 return false; app_end_pos = m_ini_str.find(L"\n[", app_pos + 2); if (app_end_pos != wstring::npos) app_end_pos++; key_pos = m_ini_str.find(wstring(L"\n") + KeyName + L' ', app_pos); //查找“\nkey_name ” if (key_pos >= app_end_pos) //如果找不到“\nkey_name ”,则查找“\nkey_name=” key_pos = m_ini_str.find(wstring(L"\n") + KeyName + L'=', app_pos); if (key_pos >= app_end_pos) //找不到KeyName,返回默认字符串 { return false; } else //找到了KeyName,获取等号到换行符之间的文本 { size_t str_pos; str_pos = m_ini_str.find(L'=', key_pos + 2); size_t line_end_pos = m_ini_str.find(L'\n', key_pos + 2); if (str_pos > line_end_pos) //所在行没有等号,返回默认字符串 { return false; } else { str_pos++; } size_t str_end_pos; str_end_pos = m_ini_str.find(L"\n", str_pos); //获取文本 wstring return_str{ m_ini_str.substr(str_pos, str_end_pos - str_pos) }; //如果前后有空格,则将其删除 CCommon::StringNormalize(return_str); str = return_str; return true; } } wstring CIniHelper::MergeStringList(const vector& values) { wstring str_merge; int index = 0; //在每个字符串前后加上引号,再将它们用逗号连接起来 for (const wstring& str : values) { if (index > 0) str_merge.push_back(L','); str_merge.push_back(L'\"'); str_merge += str; str_merge.push_back(L'\"'); index++; } return str_merge; } void CIniHelper::SplitStringList(vector& values, const wstring& str_value) { CCommon::StringSplit(str_value, wstring(L"\",\""), values); if (!values.empty()) { //结果中第一项前面和最后一项的后面各还有一个引号,将它们删除 values.front() = values.front().substr(1); values.back().pop_back(); } } ================================================ FILE: TrafficMonitor/IniHelper.h ================================================ //ini读写类 //使用时将ini文件路径通过构造函数参数传递 //在向ini文件写入数据时,需要在最后调用Save()函数以将更改保存到文件 //默认以UTF8_BOM编码保存,如果要以ANSI保存,请调用SetSaveAsUTF8(false); #pragma once class CIniHelper { public: // 从磁盘加载ini文件 // file_path:文件路径 // force_utf8:如果为true,则强制以UTF8编码解析,否则,仅当含有UTF8 BOM时才以UTF8编码解析 CIniHelper(const wstring& file_path, bool force_utf8 = false); // 从资源文件加载ini (只能读取) CIniHelper(UINT id, bool is_utf8 = true); CIniHelper(); ~CIniHelper(); void FromDirectString(const wstring& str_content); void SetSaveAsUTF8(bool utf8); void WriteString(const wchar_t* AppName, const wchar_t* KeyName, const wstring& str); wstring GetString(const wchar_t* AppName, const wchar_t* KeyName, const wchar_t* default_str) const; bool GetString(const wchar_t* AppName, const wchar_t* KeyName, wstring& str) const; void WriteInt(const wchar_t * AppName, const wchar_t * KeyName, int value); int GetInt(const wchar_t * AppName, const wchar_t * KeyName, int default_value) const; void WriteBool(const wchar_t * AppName, const wchar_t * KeyName, bool value); bool GetBool(const wchar_t * AppName, const wchar_t * KeyName, bool default_value) const; void WriteIntArray(const wchar_t * AppName, const wchar_t * KeyName, const int* values, int size); //写入一个int数组,元素个数为size void GetIntArray(const wchar_t * AppName, const wchar_t * KeyName, int* values, int size, int default_value = 0) const; //读取一个int数组,储存到values,元素个数为size void WriteBoolArray(const wchar_t * AppName, const wchar_t * KeyName, const bool* values, int size); void GetBoolArray(const wchar_t * AppName, const wchar_t * KeyName, bool* values, int size, bool default_value = false) const; void WriteStringList(const wchar_t* AppName, const wchar_t* KeyName, const vector& values); //写入一个字符串列表,由于保存到ini文件中时字符串前后会加上引号,所以字符串中不能包含引号 void GetStringList(const wchar_t* AppName, const wchar_t* KeyName, vector& values, const vector& default_value) const; // 获取带有指定前缀的所有AppName(不含前缀) vector GetAllAppName(const wstring& prefix) const; // 获取一个AppName下所有键值对 void GetAllKeyValues(const wstring& AppName, std::map& map) const; //移除一个段 bool RemoveSection(const wstring& AppName); bool Save(); //将ini文件保存到文件,成功返回true protected: wstring m_file_path; wstring m_ini_str; bool m_save_as_utf8{ true }; //是否以及UTF8编码保存 static void UnEscapeString(wstring& str); void _WriteString(const wchar_t* AppName, const wchar_t* KeyName, const wstring& str); bool _GetString(const wchar_t* AppName, const wchar_t* KeyName, wstring& str) const; static wstring MergeStringList(const vector& values); static void SplitStringList(vector& values, const wstring& str_value); }; ================================================ FILE: TrafficMonitor/LinkStatic.cpp ================================================ // CLinkStatic.cpp: 实现文件 #include "stdafx.h" #include "TrafficMonitor.h" #include "LinkStatic.h" // CLinkStatic IMPLEMENT_DYNAMIC(CLinkStatic, CStatic) CLinkStatic::CLinkStatic() { } CLinkStatic::~CLinkStatic() { } void CLinkStatic::SetBackgroundColor(COLORREF background_color) { m_back_color = background_color; } void CLinkStatic::SetURL(CString strURL) { m_strURL = strURL; } CString CLinkStatic::GetURL() const { return m_strURL; } BEGIN_MESSAGE_MAP(CLinkStatic, CStatic) ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_MOUSEHOVER() ON_WM_MOUSELEAVE() ON_WM_SETCURSOR() ON_WM_PAINT() END_MESSAGE_MAP() // CLinkStatic 消息处理程序 bool CLinkStatic::IsLinkValid() const { return (!m_link_is_url || !m_strURL.IsEmpty()); } void CLinkStatic::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_LEAVE | TME_HOVER; tme.dwHoverTime = 1; _TrackMouseEvent(&tme); } void CLinkStatic::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (m_link_is_url) { if (!m_strURL.IsEmpty()) ShellExecute(NULL, _T("open"), m_strURL, NULL, NULL, SW_SHOW); //打开超链接 } else { CWnd* pParent{ GetParent() }; if (pParent != nullptr) pParent->SendMessage(WM_LINK_CLICKED, (WPARAM)this); } } void CLinkStatic::OnMouseHover(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (!m_bHot) { m_bHot = true; Invalidate(); } else { CStatic::OnMouseHover(nFlags, point); } } void CLinkStatic::OnMouseLeave() { // TODO: 在此添加消息处理程序代码和/或调用默认值 m_bHot = false; Invalidate(); } BOOL CLinkStatic::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (IsLinkValid() && m_bHot) { ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(32649))); return TRUE; } return CStatic::OnSetCursor(pWnd, nHitTest, message); } void CLinkStatic::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CStatic::OnPaint() //准备工作 if (m_hover_font.GetSafeHandle() == NULL) { LOGFONT lf; GetFont()->GetLogFont(&lf); lf.lfUnderline = true; m_hover_font.CreateFontIndirect(&lf); } if (IsLinkValid()) { if (m_bHot) dc.SelectObject(&m_hover_font); else dc.SelectObject(GetFont()); dc.SetTextColor(GetSysColor(COLOR_HOTLIGHT)); } else { dc.SelectObject(GetFont()); } dc.SetBkMode(TRANSPARENT); CRect rect; this->GetClientRect(&rect); //画背景 dc.FillSolidRect(rect, m_back_color); ///输出文字 CString text; GetWindowText(text); if (text.GetLength()>0) { dc.DrawText(text, rect, DT_VCENTER | DT_SINGLELINE); } } void CLinkStatic::PreSubclassWindow() { // TODO: 在此添加专用代码和/或调用基类 DWORD dwStyle = GetStyle(); ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY); CStatic::PreSubclassWindow(); } ================================================ FILE: TrafficMonitor/LinkStatic.h ================================================ /*继承于CStatic类,用作超链接: 调用SetURL函数设置超链接; 调用SetLinkIsURL函数设置点击控件后是打开超链接还是响应命令 如果调用SetLinkIsURL(false),则点击控件后会向父窗口发送一个WM_LINK_CLICKED消息, 并通过WPARAM传递控件的指针。 否则,点击控件后打开超链接 */ #pragma once #define WM_LINK_CLICKED (WM_USER + 1002) // CLinkStatic class CLinkStatic : public CStatic { DECLARE_DYNAMIC(CLinkStatic) public: CLinkStatic(); virtual ~CLinkStatic(); void SetBackgroundColor(COLORREF background_color); public: void SetURL(CString strURL); //设置超链接 CString GetURL() const; //获取超链接字符串 void SetLinkIsURL(bool enable) { m_link_is_url = enable; } protected: bool m_link_is_url{ true }; //如果为true,点击后打开超链接,否则向父窗口发送一个点击消息 bool m_bHot{ false }; //当鼠标指向超链接时,则为true CString m_strURL; //超链接字符串 COLORREF m_back_color{ GetSysColor(COLOR_BTNFACE) }; CFont m_hover_font; bool IsLinkValid() const; protected: DECLARE_MESSAGE_MAP() afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseHover(UINT nFlags, CPoint point); afx_msg void OnMouseLeave(); afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); public: afx_msg void OnPaint(); virtual void PreSubclassWindow(); }; ================================================ FILE: TrafficMonitor/ListCtrlEx.cpp ================================================ #include "stdafx.h" #include "ListCtrlEx.h" IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl) CListCtrlEx::CListCtrlEx() { } CListCtrlEx::~CListCtrlEx() { } void CListCtrlEx::Edit(int row, int col) { EnsureVisible(row, FALSE); //编辑一行时确保该行可见 m_editing = true; m_edit_row = row; m_edit_col = col; CRect item_rect; GetSubItemRect(row, col, LVIR_LABEL, item_rect); //取得子项的矩形 CString text = GetItemText(row, col); //取得子项的内容 m_item_edit.SetWindowText(text); //将子项的内容显示到编辑框中 m_item_edit.ShowWindow(SW_SHOW); //显示编辑框 m_item_edit.MoveWindow(item_rect); //将编辑框移动到子项上面,覆盖在子项上 m_item_edit.SetFocus(); //使编辑框取得焦点 m_item_edit.SetSel(0, -1); } void CListCtrlEx::SetEditColMethod(eEditColMethod method) { m_edit_col_method = method; } void CListCtrlEx::SetEditableCol(const std::initializer_list& paras) { m_edit_cols = paras; } void CListCtrlEx::EndEdit() { if (m_editing) { if (m_edit_row >= 0 && m_edit_row < GetItemCount()) { CString str; m_item_edit.GetWindowText(str); //取得编辑框的内容 SetItemText(m_edit_row, m_edit_col, str); //将该内容更新到CListCtrl中 } m_item_edit.ShowWindow(SW_HIDE);//隐藏编辑框 m_editing = false; } } BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl) ON_EN_KILLFOCUS(IDC_ITEM_EDITBOX, &CListCtrlEx::OnEnKillfocusEdit1) ON_NOTIFY_REFLECT_EX(NM_DBLCLK, &CListCtrlEx::OnNMDblclk) ON_NOTIFY_REFLECT(LVN_BEGINSCROLL, &CListCtrlEx::OnLvnBeginScroll) ON_MESSAGE(WM_TABLET_QUERYSYSTEMGESTURESTATUS, &CListCtrlEx::OnTabletQuerysystemgesturestatus) END_MESSAGE_MAP() void CListCtrlEx::OnEnKillfocusEdit1() { //当文本编辑控件控件失去焦点时响应 EndEdit(); } void CListCtrlEx::PreSubclassWindow() { // TODO: 在此添加专用代码和/或调用基类 m_item_edit.Create(WS_BORDER | ES_AUTOHSCROLL, CRect(), this, IDC_ITEM_EDITBOX); m_item_edit.SetFont(GetFont()); CListCtrl::PreSubclassWindow(); } BOOL CListCtrlEx::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 //如果双击的列是要编辑的列 if (m_edit_col_method == EC_ALL || (m_edit_col_method == EC_SPECIFIED && m_edit_cols.find(pNMItemActivate->iSubItem) != m_edit_cols.end())) { Edit(pNMItemActivate->iItem, pNMItemActivate->iSubItem); *pResult = 0; return TRUE; } else { return FALSE; } } void CListCtrlEx::OnLvnBeginScroll(NMHDR *pNMHDR, LRESULT *pResult) { // 此功能要求 Internet Explorer 5.5 或更高版本。 // 符号 _WIN32_IE 必须是 >= 0x0560。 LPNMLVSCROLL pStateChanged = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 EndEdit(); *pResult = 0; } afx_msg LRESULT CListCtrlEx::OnTabletQuerysystemgesturestatus(WPARAM wParam, LPARAM lParam) { return 0; } BOOL CListCtrlEx::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) { //编辑状态下按回车键结束编辑而不是关闭对话框 if (m_editing) { EndEdit(); return TRUE; } } return CListCtrl::PreTranslateMessage(pMsg); } ================================================ FILE: TrafficMonitor/ListCtrlEx.h ================================================ #pragma once #include "afxcmn.h" #include "DrawCommon.h" #include #define IDC_ITEM_EDITBOX 1991 class CListCtrlEx : public CListCtrl { DECLARE_DYNAMIC(CListCtrlEx) public: CListCtrlEx(); ~CListCtrlEx(); void Edit(int row, int col); //编辑指定单元格 enum eEditColMethod //要编辑的列的方式 { EC_NONE, //无 EC_ALL, //全部 EC_SPECIFIED //指定的列 }; void SetEditColMethod(eEditColMethod method); void SetEditableCol(const std::initializer_list& paras); //设置允许编辑的列 private: CEdit m_item_edit; int m_edit_row{}; int m_edit_col{}; bool m_editing{}; eEditColMethod m_edit_col_method{ EC_NONE }; std::set m_edit_cols; protected: void EndEdit(); DECLARE_MESSAGE_MAP() afx_msg void OnEnKillfocusEdit1(); virtual void PreSubclassWindow(); afx_msg BOOL OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnLvnBeginScroll(NMHDR *pNMHDR, LRESULT *pResult); afx_msg LRESULT OnTabletQuerysystemgesturestatus(WPARAM wParam, LPARAM lParam); public: virtual BOOL PreTranslateMessage(MSG* pMsg); }; ================================================ FILE: TrafficMonitor/MainWndColorDlg.cpp ================================================ // MainWndColorDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "MainWndColorDlg.h" #include "afxdialogex.h" #include "CMFCColorDialogEx.h" #include "TrafficMonitorDlg.h" // CMainWndColorDlg 对话框 IMPLEMENT_DYNAMIC(CMainWndColorDlg, CBaseDialog) CMainWndColorDlg::CMainWndColorDlg(const std::map& colors, CWnd* pParent /*=NULL*/) : CBaseDialog(IDD_MAIN_COLOR_DIALOG, pParent), m_colors(colors) { } CMainWndColorDlg::~CMainWndColorDlg() { } CString CMainWndColorDlg::GetDialogName() const { return _T("MainWndColorDlg"); } void CMainWndColorDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_list_ctrl); } BEGIN_MESSAGE_MAP(CMainWndColorDlg, CBaseDialog) ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &CMainWndColorDlg::OnNMDblclkList1) END_MESSAGE_MAP() // CMainWndColorDlg 消息处理程序 BOOL CMainWndColorDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_MAIN_WINDOW), FALSE); // 设置小图标 //初始化列表控件 CRect rect; m_list_ctrl.GetClientRect(rect); m_list_ctrl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); int width0, width1; width0 = rect.Width() * 2 / 3; width1 = rect.Width() - width0 - theApp.DPI(20) - 1; m_list_ctrl.InsertColumn(0, CCommon::LoadText(IDS_ITEM), LVCFMT_LEFT, width0); //插入第0列 m_list_ctrl.InsertColumn(1, CCommon::LoadText(IDS_COLOR), LVCFMT_LEFT, width1); //插入第1列 m_list_ctrl.SetDrawItemRangMargin(theApp.DPI(2)); static std::set all_skin_items; CTrafficMonitorDlg::Instance()->GetCurSkin().GetSkinDisplayItems(all_skin_items); //向列表中插入行 for (auto iter = all_skin_items.begin(); iter != all_skin_items.end(); ++iter) { CString item_name = iter->GetItemName(); if (!item_name.IsEmpty()) { int index = m_list_ctrl.GetItemCount(); m_list_ctrl.InsertItem(index, item_name); m_list_ctrl.SetItemColor(index, 1, m_colors[*iter]); m_list_ctrl.SetItemData(index, (DWORD_PTR)&(*iter)); } } return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CMainWndColorDlg::OnNMDblclkList1(NMHDR *pNMHDR, LRESULT *pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 int index = pNMItemActivate->iItem; COLORREF color = m_list_ctrl.GetItemColor(index, 1); CMFCColorDialogEx colorDlg(color, 0, this); if (colorDlg.DoModal() == IDOK) { color = colorDlg.GetColor(); m_list_ctrl.SetItemColor(index, 1, color); CommonDisplayItem* item = (CommonDisplayItem*)(m_list_ctrl.GetItemData(index)); m_colors[*item] = color; } *pResult = 0; } ================================================ FILE: TrafficMonitor/MainWndColorDlg.h ================================================ #pragma once #include "ColorStatic.h" #include "afxwin.h" #include "ColorSettingListCtrl.h" #include "BaseDialog.h" // CMainWndColorDlg 对话框 class CMainWndColorDlg : public CBaseDialog { DECLARE_DYNAMIC(CMainWndColorDlg) public: CMainWndColorDlg(const std::map& colors, CWnd* pParent = NULL); // 标准构造函数 virtual ~CMainWndColorDlg(); const std::map& GetColors() const { return m_colors; } // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MAIN_COLOR_DIALOG }; #endif protected: std::map m_colors; CColorSettingListCtrl m_list_ctrl; virtual CString GetDialogName() const override; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnNMDblclkList1(NMHDR *pNMHDR, LRESULT *pResult); }; ================================================ FILE: TrafficMonitor/MainWndSettingsDlg.cpp ================================================ // MainWndSettingsDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "MainWndSettingsDlg.h" #include "afxdialogex.h" #include "CMFCColorDialogEx.h" #include "DisplayTextSettingDlg.h" #include "FileDialogEx.h" #include "TrafficMonitorDlg.h" #include "SkinManager.h" // CMainWndSettingsDlg 对话框 IMPLEMENT_DYNAMIC(CMainWndSettingsDlg, CTabDlg) CMainWndSettingsDlg::CMainWndSettingsDlg(CWnd* pParent /*=NULL*/) : CTabDlg(IDD_MAIN_WND_SETTINGS_DIALOG, pParent) { } CMainWndSettingsDlg::~CMainWndSettingsDlg() { } void CMainWndSettingsDlg::SetControlMouseWheelEnable(bool enable) { m_unit_combo.SetMouseWheelEnable(enable); m_double_click_combo.SetMouseWheelEnable(enable); m_font_size_edit.SetMouseWheelEnable(enable); m_memory_display_combo.SetMouseWheelEnable(enable); } bool CMainWndSettingsDlg::InitializeControls() { RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_FONT_STATIC }, { CtrlTextInfo::C0, IDC_FONT_NAME_EDIT }, { CtrlTextInfo::R1, IDC_FONT_SIZE_STATIC }, { CtrlTextInfo::R2, IDC_FONT_SIZE_EDIT }, { CtrlTextInfo::R3, IDC_SET_FONT_BUTTON, CtrlTextInfo::W16 } }); RepositionTextBasedControls({ { CtrlTextInfo::L2, IDC_TXT_COLOR_LABEL_STATIC }, { CtrlTextInfo::L1, IDC_TEXT_COLOR_STATIC }, { CtrlTextInfo::C0, IDC_SPECIFY_EACH_ITEM_COLOR_CHECK, CtrlTextInfo::W16 }, { CtrlTextInfo::R1, IDC_RESOTRE_SKIN_DEFAULT_BUTTON, CtrlTextInfo::W16 } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_DISPLAY_TEXT_SETTING_BUTTON, CtrlTextInfo::W16 } }); RepositionTextBasedControls({ { CtrlTextInfo::L1, IDC_MEMORY_DISPLAY_MODE_STATIC }, { CtrlTextInfo::C0, IDC_MEMORY_DISPLAY_COMBO } }); RepositionTextBasedControls({ { CtrlTextInfo::L1, IDC_DOUBLE_CLICK_ACTION_STATIC }, { CtrlTextInfo::C0, IDC_DOUBLE_CLICK_COMBO }, { CtrlTextInfo::L1, IDC_EXE_PATH_STATIC }, { CtrlTextInfo::C0, IDC_EXE_PATH_EDIT }, { CtrlTextInfo::R1, IDC_BROWSE_BUTTON } }); return true; } void CMainWndSettingsDlg::DrawStaticColor() { //CCommon::FillStaticColor(m_color_static, m_data.text_color); if (m_data.text_colors.empty()) return; if (m_data.specify_each_item_color) { int color_num{ static_cast(m_data.text_colors.size()) }; if (color_num > 16) color_num = 16; m_color_static.SetColorNum(color_num); int index{}; for (const auto& item : m_data.text_colors) { m_color_static.SetFillColor(index, item.second); index++; } m_color_static.Invalidate(); } else { m_color_static.SetFillColor(m_data.text_colors.begin()->second); } } void CMainWndSettingsDlg::IniUnitCombo() { m_unit_combo.ResetContent(); m_unit_combo.AddString(CCommon::LoadText(IDS_AUTO)); if (m_data.unit_byte) { m_unit_combo.AddString(CCommon::LoadText(IDS_FIXED_AS, _T(" KB/s"))); m_unit_combo.AddString(CCommon::LoadText(IDS_FIXED_AS, _T(" MB/s"))); } else { m_unit_combo.AddString(CCommon::LoadText(IDS_FIXED_AS, _T(" Kb/s"))); m_unit_combo.AddString(CCommon::LoadText(IDS_FIXED_AS, _T(" Mb/s"))); } m_unit_combo.SetCurSel(static_cast(m_data.speed_unit)); } void CMainWndSettingsDlg::EnableControl() { bool exe_path_enable = (m_data.double_click_action == DoubleClickAction::SEPCIFIC_APP); CWnd* pWnd{}; pWnd = GetDlgItem(IDC_EXE_PATH_STATIC); if (pWnd != nullptr) pWnd->ShowWindow(exe_path_enable ? SW_SHOW : SW_HIDE); pWnd = GetDlgItem(IDC_EXE_PATH_EDIT); if (pWnd != nullptr) pWnd->ShowWindow(exe_path_enable ? SW_SHOW : SW_HIDE); pWnd = GetDlgItem(IDC_BROWSE_BUTTON); if (pWnd != nullptr) pWnd->ShowWindow(exe_path_enable ? SW_SHOW : SW_HIDE); } void CMainWndSettingsDlg::DoDataExchange(CDataExchange* pDX) { DDX_Control(pDX, IDC_TEXT_COLOR_STATIC, m_color_static); CTabDlg::DoDataExchange(pDX); DDX_Control(pDX, IDC_HIDE_UNIT_CHECK, m_hide_unit_chk); DDX_Control(pDX, IDC_UNIT_COMBO, m_unit_combo); DDX_Control(pDX, IDC_FONT_SIZE_EDIT, m_font_size_edit); DDX_Control(pDX, IDC_DOUBLE_CLICK_COMBO, m_double_click_combo); DDX_Control(pDX, IDC_MEMORY_DISPLAY_COMBO, m_memory_display_combo); } BEGIN_MESSAGE_MAP(CMainWndSettingsDlg, CTabDlg) //ON_EN_CHANGE(IDC_UPLOAD_EDIT, &CMainWndSettingsDlg::OnEnChangeUploadEdit) //ON_EN_CHANGE(IDC_DOWNLOAD_EDIT, &CMainWndSettingsDlg::OnEnChangeDownloadEdit) //ON_EN_CHANGE(IDC_CPU_EDIT, &CMainWndSettingsDlg::OnEnChangeCpuEdit) //ON_EN_CHANGE(IDC_MEMORY_EDIT, &CMainWndSettingsDlg::OnEnChangeMemoryEdit) //ON_BN_CLICKED(IDC_SET_COLOR_BUTTON1, &CMainWndSettingsDlg::OnBnClickedSetColorButton1) //ON_BN_CLICKED(IDC_SET_DEFAULT_BUTTON, &CMainWndSettingsDlg::OnBnClickedSetDefaultButton) ON_BN_CLICKED(IDC_SET_FONT_BUTTON, &CMainWndSettingsDlg::OnBnClickedSetFontButton) ON_BN_CLICKED(IDC_SWITCH_UP_DOWN_CHECK, &CMainWndSettingsDlg::OnBnClickedSwitchUpDownCheck) ON_BN_CLICKED(IDC_FULLSCREEN_HIDE_CHECK, &CMainWndSettingsDlg::OnBnClickedFullscreenHideCheck) ON_BN_CLICKED(IDC_SPEED_SHORT_MODE_CHECK2, &CMainWndSettingsDlg::OnBnClickedSpeedShortModeCheck2) ON_CBN_SELCHANGE(IDC_UNIT_COMBO, &CMainWndSettingsDlg::OnCbnSelchangeUnitCombo) ON_BN_CLICKED(IDC_HIDE_UNIT_CHECK, &CMainWndSettingsDlg::OnBnClickedHideUnitCheck) ON_BN_CLICKED(IDC_HIDE_PERCENTAGE_CHECK, &CMainWndSettingsDlg::OnBnClickedHidePercentageCheck) ON_MESSAGE(WM_STATIC_CLICKED, &CMainWndSettingsDlg::OnStaticClicked) ON_BN_CLICKED(IDC_SPECIFY_EACH_ITEM_COLOR_CHECK, &CMainWndSettingsDlg::OnBnClickedSpecifyEachItemColorCheck) ON_CBN_SELCHANGE(IDC_DOUBLE_CLICK_COMBO, &CMainWndSettingsDlg::OnCbnSelchangeDoubleClickCombo) ON_BN_CLICKED(IDC_SEPARATE_VALUE_UNIT_CHECK, &CMainWndSettingsDlg::OnBnClickedSeparateValueUnitCheck) ON_BN_CLICKED(IDC_UNIT_BYTE_RADIO, &CMainWndSettingsDlg::OnBnClickedUnitByteRadio) ON_BN_CLICKED(IDC_UNIT_BIT_RADIO, &CMainWndSettingsDlg::OnBnClickedUnitBitRadio) ON_BN_CLICKED(IDC_SHOW_TOOL_TIP_CHK, &CMainWndSettingsDlg::OnBnClickedShowToolTipChk) ON_BN_CLICKED(IDC_BROWSE_BUTTON, &CMainWndSettingsDlg::OnBnClickedBrowseButton) ON_BN_CLICKED(IDC_DISPLAY_TEXT_SETTING_BUTTON, &CMainWndSettingsDlg::OnBnClickedDisplayTextSettingButton) ON_CBN_SELCHANGE(IDC_MEMORY_DISPLAY_COMBO, &CMainWndSettingsDlg::OnCbnSelchangeMemoryDisplayCombo) ON_BN_CLICKED(IDC_ALWAYS_ON_TOP_CHECK, &CMainWndSettingsDlg::OnBnClickedAlwaysOnTopCheck) ON_BN_CLICKED(IDC_MOUSE_PENETRATE_CHECK, &CMainWndSettingsDlg::OnBnClickedMousePenetrateCheck) ON_BN_CLICKED(IDC_LOCK_WINDOW_POS_CHECK, &CMainWndSettingsDlg::OnBnClickedLockWindowPosCheck) ON_BN_CLICKED(IDC_ALOW_OUT_OF_BORDER_CHECK, &CMainWndSettingsDlg::OnBnClickedAlowOutOfBorderCheck) ON_BN_CLICKED(IDC_RESOTRE_SKIN_DEFAULT_BUTTON, &CMainWndSettingsDlg::OnBnClickedResotreSkinDefaultButton) ON_EN_CHANGE(IDC_FONT_SIZE_EDIT, &CMainWndSettingsDlg::OnEnChangeFontSizeEdit) END_MESSAGE_MAP() // CMainWndSettingsDlg 消息处理程序 BOOL CMainWndSettingsDlg::OnInitDialog() { CTabDlg::OnInitDialog(); // TODO: 在此添加额外的初始化 //初始化各控件状态 SetDlgItemText(IDC_FONT_NAME_EDIT, m_data.font.name); //wchar_t buff[16]; //swprintf_s(buff, L"%d", m_data.font_size); //SetDlgItemText(IDC_FONT_SIZE_EDIT, buff); m_font_size_edit.SetRange(5, 72); m_font_size_edit.SetValue(m_data.font.size); //SetDlgItemText(IDC_UPLOAD_EDIT, m_data.disp_str.Get(TDI_UP).c_str()); //SetDlgItemText(IDC_DOWNLOAD_EDIT, m_data.disp_str.Get(TDI_DOWN).c_str()); //SetDlgItemText(IDC_CPU_EDIT, m_data.disp_str.Get(TDI_CPU).c_str()); //SetDlgItemText(IDC_MEMORY_EDIT, m_data.disp_str.Get(TDI_MEMORY).c_str()); ((CButton*)GetDlgItem(IDC_SWITCH_UP_DOWN_CHECK))->SetCheck(m_data.swap_up_down); ((CButton*)GetDlgItem(IDC_FULLSCREEN_HIDE_CHECK))->SetCheck(m_data.hide_main_wnd_when_fullscreen); ((CButton*)GetDlgItem(IDC_SPEED_SHORT_MODE_CHECK2))->SetCheck(m_data.speed_short_mode); ((CButton*)GetDlgItem(IDC_SEPARATE_VALUE_UNIT_CHECK))->SetCheck(m_data.separate_value_unit_with_space); ((CButton*)GetDlgItem(IDC_SHOW_TOOL_TIP_CHK))->SetCheck(m_data.show_tool_tip); m_color_static.SetLinkCursor(); DrawStaticColor(); m_toolTip.Create(this); m_toolTip.SetMaxTipWidth(theApp.DPI(300)); m_toolTip.AddTool(GetDlgItem(IDC_SPEED_SHORT_MODE_CHECK2), CCommon::LoadText(IDS_SPEED_SHORT_MODE_TIP)); if (m_data.unit_byte) ((CButton*)GetDlgItem(IDC_UNIT_BYTE_RADIO))->SetCheck(TRUE); else ((CButton*)GetDlgItem(IDC_UNIT_BIT_RADIO))->SetCheck(TRUE); IniUnitCombo(); m_hide_unit_chk.SetCheck(m_data.hide_unit); if (m_data.speed_unit == SpeedUnit::AUTO) { m_hide_unit_chk.SetCheck(FALSE); m_data.hide_unit = false; m_hide_unit_chk.EnableWindow(FALSE); } ((CButton*)GetDlgItem(IDC_HIDE_PERCENTAGE_CHECK))->SetCheck(m_data.hide_percent); if (m_text_disable) { //GetDlgItem(IDC_UPLOAD_EDIT)->EnableWindow(FALSE); //GetDlgItem(IDC_DOWNLOAD_EDIT)->EnableWindow(FALSE); //GetDlgItem(IDC_CPU_EDIT)->EnableWindow(FALSE); //GetDlgItem(IDC_MEMORY_EDIT)->EnableWindow(FALSE); EnableDlgCtrl(IDC_DISPLAY_TEXT_SETTING_BUTTON, false); m_data.swap_up_down = false; ((CButton*)GetDlgItem(IDC_SWITCH_UP_DOWN_CHECK))->SetCheck(FALSE); GetDlgItem(IDC_SWITCH_UP_DOWN_CHECK)->EnableWindow(FALSE); //GetDlgItem(IDC_SET_DEFAULT_BUTTON)->EnableWindow(FALSE); } CheckDlgButton(IDC_SPECIFY_EACH_ITEM_COLOR_CHECK, m_data.specify_each_item_color); m_double_click_combo.AddString(CCommon::LoadText(IDS_OPEN_CONNECTION_DETIAL)); m_double_click_combo.AddString(CCommon::LoadText(IDS_OPEN_HISTORICAL_TRAFFIC)); m_double_click_combo.AddString(CCommon::LoadText(IDS_SHOW_HIDE_MORE_INFO)); m_double_click_combo.AddString(CCommon::LoadText(IDS_OPEN_OPTION_SETTINGS)); m_double_click_combo.AddString(CCommon::LoadText(IDS_OPEN_TASK_MANAGER)); m_double_click_combo.AddString(CCommon::LoadText(IDS_SPECIFIC_APP)); m_double_click_combo.AddString(CCommon::LoadText(IDS_CHANGE_SKIN)); m_double_click_combo.AddString(CCommon::LoadText(IDS_NONE)); m_double_click_combo.SetCurSel(static_cast(m_data.double_click_action)); SetDlgItemText(IDC_EXE_PATH_EDIT, m_data.double_click_exe.c_str()); EnableControl(); //初始化内存显示方式下拉列表 m_memory_display_combo.AddString(CCommon::LoadText(IDS_USAGE_PERCENTAGE)); m_memory_display_combo.AddString(CCommon::LoadText(IDS_MEMORY_USED)); m_memory_display_combo.AddString(CCommon::LoadText(IDS_MEMORY_AVAILABLE)); m_memory_display_combo.SetCurSel(static_cast(m_data.memory_display)); CheckDlgButton(IDC_ALWAYS_ON_TOP_CHECK, m_data.m_always_on_top); CheckDlgButton(IDC_MOUSE_PENETRATE_CHECK, m_data.m_mouse_penetrate); CheckDlgButton(IDC_LOCK_WINDOW_POS_CHECK, m_data.m_lock_window_pos); CheckDlgButton(IDC_ALOW_OUT_OF_BORDER_CHECK, m_data.m_alow_out_of_border); ////设置控件不响应鼠标滚轮消息 //m_unit_combo.SetMouseWheelEnable(false); //m_double_click_combo.SetMouseWheelEnable(false); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } //void CMainWndSettingsDlg::OnEnChangeUploadEdit() //{ // // TODO: 如果该控件是 RICHEDIT 控件,它将不 // // 发送此通知,除非重写 CTabDlg::OnInitDialog() // // 函数并调用 CRichEditCtrl().SetEventMask(), // // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // // // TODO: 在此添加控件通知处理程序代码 // CString tmp; // GetDlgItemText(IDC_UPLOAD_EDIT, tmp); // m_data.disp_str.Get(TDI_UP) = tmp; //} // // //void CMainWndSettingsDlg::OnEnChangeDownloadEdit() //{ // // TODO: 如果该控件是 RICHEDIT 控件,它将不 // // 发送此通知,除非重写 CTabDlg::OnInitDialog() // // 函数并调用 CRichEditCtrl().SetEventMask(), // // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // // // TODO: 在此添加控件通知处理程序代码 // CString tmp; // GetDlgItemText(IDC_DOWNLOAD_EDIT, tmp); // m_data.disp_str.Get(TDI_DOWN) = tmp; //} // // //void CMainWndSettingsDlg::OnEnChangeCpuEdit() //{ // // TODO: 如果该控件是 RICHEDIT 控件,它将不 // // 发送此通知,除非重写 CTabDlg::OnInitDialog() // // 函数并调用 CRichEditCtrl().SetEventMask(), // // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // // // TODO: 在此添加控件通知处理程序代码 // CString tmp; // GetDlgItemText(IDC_CPU_EDIT, tmp); // m_data.disp_str.Get(TDI_CPU) = tmp; //} // // //void CMainWndSettingsDlg::OnEnChangeMemoryEdit() //{ // // TODO: 如果该控件是 RICHEDIT 控件,它将不 // // 发送此通知,除非重写 CTabDlg::OnInitDialog() // // 函数并调用 CRichEditCtrl().SetEventMask(), // // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // // // TODO: 在此添加控件通知处理程序代码 // CString tmp; // GetDlgItemText(IDC_MEMORY_EDIT, tmp); // m_data.disp_str.Get(TDI_MEMORY) = tmp; //} //void CMainWndSettingsDlg::OnBnClickedSetDefaultButton() //{ // // TODO: 在此添加控件通知处理程序代码 // m_data.disp_str.Get(TDI_UP) = CCommon::LoadText(IDS_UPLOAD_DISP, _T(": ")); // m_data.disp_str.Get(TDI_DOWN) = CCommon::LoadText(IDS_DOWNLOAD_DISP, _T(": ")); // m_data.disp_str.Get(TDI_CPU) = L"CPU: "; // m_data.disp_str.Get(TDI_MEMORY) = CCommon::LoadText(IDS_MEMORY_DISP, _T(": ")); // SetDlgItemText(IDC_UPLOAD_EDIT, m_data.disp_str.Get(TDI_UP).c_str()); // SetDlgItemText(IDC_DOWNLOAD_EDIT, m_data.disp_str.Get(TDI_DOWN).c_str()); // SetDlgItemText(IDC_CPU_EDIT, m_data.disp_str.Get(TDI_CPU).c_str()); // SetDlgItemText(IDC_MEMORY_EDIT, m_data.disp_str.Get(TDI_MEMORY).c_str()); //} void CMainWndSettingsDlg::OnBnClickedSetFontButton() { // TODO: 在此添加控件通知处理程序代码 LOGFONT lf{}; lf.lfHeight = FontSizeToLfHeight(m_data.font.size); lf.lfWeight = (m_data.font.bold ? FW_BOLD : FW_NORMAL); lf.lfItalic = m_data.font.italic; lf.lfUnderline = m_data.font.underline; lf.lfStrikeOut = m_data.font.strike_out; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS; //wcsncpy_s(lf.lfFaceName, m_data.font.name.GetString(), 32); CCommon::WStringCopy(lf.lfFaceName, 32, m_data.font.name.GetString()); CCommon::NormalizeFont(lf); CFontDialog fontDlg(&lf); //构造字体对话框,初始选择字体为之前字体 if (IDOK == fontDlg.DoModal()) // 显示字体对话框 { //获取字体信息 m_data.font.name = fontDlg.GetFaceName(); m_data.font.size = fontDlg.GetSize() / 10; m_data.font.bold = (fontDlg.IsBold() != FALSE); m_data.font.italic = (fontDlg.IsItalic() != FALSE); m_data.font.underline = (fontDlg.IsUnderline() != FALSE); m_data.font.strike_out = (fontDlg.IsStrikeOut() != FALSE); //将字体信息显示出来 SetDlgItemText(IDC_FONT_NAME_EDIT, m_data.font.name); SetDlgItemText(IDC_FONT_SIZE_EDIT, std::to_wstring(m_data.font.size).c_str()); } } void CMainWndSettingsDlg::OnBnClickedSwitchUpDownCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.swap_up_down = (((CButton*)GetDlgItem(IDC_SWITCH_UP_DOWN_CHECK))->GetCheck() != 0); } void CMainWndSettingsDlg::OnBnClickedFullscreenHideCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.hide_main_wnd_when_fullscreen = (((CButton*)GetDlgItem(IDC_FULLSCREEN_HIDE_CHECK))->GetCheck() != 0); } void CMainWndSettingsDlg::OnBnClickedSpeedShortModeCheck2() { // TODO: 在此添加控件通知处理程序代码 m_data.speed_short_mode = (((CButton*)GetDlgItem(IDC_SPEED_SHORT_MODE_CHECK2))->GetCheck() != 0); } void CMainWndSettingsDlg::OnCbnSelchangeUnitCombo() { // TODO: 在此添加控件通知处理程序代码 m_data.speed_unit = static_cast(m_unit_combo.GetCurSel()); if (m_data.speed_unit == SpeedUnit::AUTO) { m_hide_unit_chk.SetCheck(FALSE); m_data.hide_unit = false; m_hide_unit_chk.EnableWindow(FALSE); } else { m_hide_unit_chk.EnableWindow(TRUE); } } void CMainWndSettingsDlg::OnBnClickedHideUnitCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.hide_unit = (m_hide_unit_chk.GetCheck() != 0); } BOOL CMainWndSettingsDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_MOUSEMOVE) m_toolTip.RelayEvent(pMsg); return CTabDlg::PreTranslateMessage(pMsg); } void CMainWndSettingsDlg::OnOK() { // TODO: 在此添加专用代码和/或调用基类 //获取字体设置 int font_size; font_size = m_font_size_edit.GetValue(); if (font_size > MAX_FONT_SIZE || font_size < MIN_FONT_SIZE) { CString info; info.Format(CCommon::LoadText(IDS_FONT_SIZE_WARNING), MIN_FONT_SIZE, MAX_FONT_SIZE); MessageBox(info, NULL, MB_OK | MB_ICONWARNING); } else { m_data.font.size = font_size; } GetDlgItemText(IDC_FONT_NAME_EDIT, m_data.font.name); CTabDlg::OnOK(); } void CMainWndSettingsDlg::OnBnClickedHidePercentageCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.hide_percent = (((CButton*)GetDlgItem(IDC_HIDE_PERCENTAGE_CHECK))->GetCheck() != 0); } afx_msg LRESULT CMainWndSettingsDlg::OnStaticClicked(WPARAM wParam, LPARAM lParam) { switch (::GetDlgCtrlID(((CWnd*)wParam)->m_hWnd)) { case IDC_TEXT_COLOR_STATIC: { //设置文本颜色 if (m_data.specify_each_item_color) { CMainWndColorDlg colorDlg(m_data.text_colors); if (colorDlg.DoModal() == IDOK) { m_data.text_colors = colorDlg.GetColors(); DrawStaticColor(); } } else if (!m_data.text_colors.empty()) { CMFCColorDialogEx colorDlg(m_data.text_colors.begin()->second, 0, this); if (colorDlg.DoModal() == IDOK) { m_data.text_colors.begin()->second = colorDlg.GetColor(); DrawStaticColor(); } } break; } default: break; } return 0; } void CMainWndSettingsDlg::OnBnClickedSpecifyEachItemColorCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.specify_each_item_color = (((CButton*)GetDlgItem(IDC_SPECIFY_EACH_ITEM_COLOR_CHECK))->GetCheck() != 0); DrawStaticColor(); } void CMainWndSettingsDlg::OnCbnSelchangeDoubleClickCombo() { // TODO: 在此添加控件通知处理程序代码 m_data.double_click_action = static_cast(m_double_click_combo.GetCurSel()); EnableControl(); } void CMainWndSettingsDlg::OnBnClickedSeparateValueUnitCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.separate_value_unit_with_space = (((CButton*)GetDlgItem(IDC_SEPARATE_VALUE_UNIT_CHECK))->GetCheck() != 0); } void CMainWndSettingsDlg::OnBnClickedUnitByteRadio() { // TODO: 在此添加控件通知处理程序代码 m_data.unit_byte = true; IniUnitCombo(); } void CMainWndSettingsDlg::OnBnClickedUnitBitRadio() { // TODO: 在此添加控件通知处理程序代码 m_data.unit_byte = false; IniUnitCombo(); } void CMainWndSettingsDlg::OnBnClickedShowToolTipChk() { // TODO: 在此添加控件通知处理程序代码 m_data.show_tool_tip = (((CButton*)GetDlgItem(IDC_SHOW_TOOL_TIP_CHK))->GetCheck() != 0); } void CMainWndSettingsDlg::OnBnClickedBrowseButton() { // TODO: 在此添加控件通知处理程序代码 CString szFilter = CCommon::LoadText(IDS_EXE_FILTER); CFileDialogEx fileDlg(TRUE, NULL, szFilter); if (IDOK == fileDlg.DoModal()) { m_data.double_click_exe = fileDlg.GetPathName(); SetDlgItemText(IDC_EXE_PATH_EDIT, m_data.double_click_exe.c_str()); } } void CMainWndSettingsDlg::OnBnClickedDisplayTextSettingButton() { // TODO: 在此添加控件通知处理程序代码 CDisplayTextSettingDlg dlg(m_data.disp_str, true); dlg.DoModal(); } void CMainWndSettingsDlg::OnCbnSelchangeMemoryDisplayCombo() { // TODO: 在此添加控件通知处理程序代码 m_data.memory_display = static_cast(m_memory_display_combo.GetCurSel()); } void CMainWndSettingsDlg::OnBnClickedAlwaysOnTopCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.m_always_on_top = IsDlgButtonChecked(IDC_ALWAYS_ON_TOP_CHECK) != 0; } void CMainWndSettingsDlg::OnBnClickedMousePenetrateCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.m_mouse_penetrate = IsDlgButtonChecked(IDC_MOUSE_PENETRATE_CHECK) != 0; } void CMainWndSettingsDlg::OnBnClickedLockWindowPosCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.m_lock_window_pos = IsDlgButtonChecked(IDC_LOCK_WINDOW_POS_CHECK) != 0; } void CMainWndSettingsDlg::OnBnClickedAlowOutOfBorderCheck() { // TODO: 在此添加控件通知处理程序代码 m_data.m_alow_out_of_border = IsDlgButtonChecked(IDC_ALOW_OUT_OF_BORDER_CHECK) != 0; } void CMainWndSettingsDlg::OnBnClickedResotreSkinDefaultButton() { SkinSettingData skin_setting_data; CTrafficMonitorDlg* pMainWnd = CTrafficMonitorDlg::Instance(); if (pMainWnd != nullptr) { CSkinManager::SkinSettingDataFronSkin(skin_setting_data, pMainWnd->GetCurSkin()); m_data.text_colors = skin_setting_data.text_colors; m_data.specify_each_item_color = skin_setting_data.specify_each_item_color; m_data.font = skin_setting_data.font; SetDlgItemText(IDC_FONT_NAME_EDIT, m_data.font.name); SetDlgItemText(IDC_FONT_SIZE_EDIT, std::to_wstring(m_data.font.size).c_str()); CheckDlgButton(IDC_SPECIFY_EACH_ITEM_COLOR_CHECK, m_data.specify_each_item_color); DrawStaticColor(); } } void CMainWndSettingsDlg::OnEnChangeFontSizeEdit() { m_data.font.size = m_font_size_edit.GetValue(); } ================================================ FILE: TrafficMonitor/MainWndSettingsDlg.h ================================================ #pragma once #include "ColorStatic.h" #include "afxwin.h" #include "SpinEdit.h" #include "TabDlg.h" #include "MainWndColorDlg.h" #include "ComboBox2.h" // CMainWndSettingsDlg 对话框 class CMainWndSettingsDlg : public CTabDlg { DECLARE_DYNAMIC(CMainWndSettingsDlg) public: CMainWndSettingsDlg(CWnd* pParent = NULL); // 标准构造函数 virtual ~CMainWndSettingsDlg(); //选项设置数据 MainWndSettingData m_data; bool m_text_disable{ false }; //如果为true,则不允许设置“显示文本”,并不允许交换上传和下载的位置 // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MAIN_WND_SETTINGS_DIALOG }; #endif protected: //控件变量 CColorStatic m_color_static; CToolTipCtrl m_toolTip; CComboBox2 m_unit_combo; CButton m_hide_unit_chk; CSpinEdit m_font_size_edit; CComboBox2 m_double_click_combo; CComboBox2 m_memory_display_combo; protected: void DrawStaticColor(); void IniUnitCombo(); void EnableControl(); virtual void SetControlMouseWheelEnable(bool enable) override; virtual bool InitializeControls() override; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); //afx_msg void OnEnChangeUploadEdit(); //afx_msg void OnEnChangeDownloadEdit(); //afx_msg void OnEnChangeCpuEdit(); //afx_msg void OnEnChangeMemoryEdit(); //afx_msg void OnBnClickedSetDefaultButton(); afx_msg void OnBnClickedSetFontButton(); afx_msg void OnBnClickedSwitchUpDownCheck(); afx_msg void OnBnClickedFullscreenHideCheck(); afx_msg void OnBnClickedSpeedShortModeCheck2(); afx_msg void OnCbnSelchangeUnitCombo(); afx_msg void OnBnClickedHideUnitCheck(); virtual BOOL PreTranslateMessage(MSG* pMsg); virtual void OnOK(); afx_msg void OnBnClickedHidePercentageCheck(); protected: afx_msg LRESULT OnStaticClicked(WPARAM wParam, LPARAM lParam); public: afx_msg void OnBnClickedSpecifyEachItemColorCheck(); afx_msg void OnCbnSelchangeDoubleClickCombo(); afx_msg void OnBnClickedSeparateValueUnitCheck(); afx_msg void OnBnClickedUnitByteRadio(); afx_msg void OnBnClickedUnitBitRadio(); afx_msg void OnBnClickedShowToolTipChk(); afx_msg void OnBnClickedBrowseButton(); afx_msg void OnBnClickedDisplayTextSettingButton(); afx_msg void OnCbnSelchangeMemoryDisplayCombo(); afx_msg void OnBnClickedAlwaysOnTopCheck(); afx_msg void OnBnClickedMousePenetrateCheck(); afx_msg void OnBnClickedLockWindowPosCheck(); afx_msg void OnBnClickedAlowOutOfBorderCheck(); afx_msg void OnBnClickedResotreSkinDefaultButton(); afx_msg void OnEnChangeFontSizeEdit(); }; ================================================ FILE: TrafficMonitor/MessageDlg.cpp ================================================ // HelpDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "MessageDlg.h" #include "DrawCommon.h" // CMessageDlg 对话框 #define MESSAGE_DLG_ICON_SIZE (theApp.DPI(32)) #define MESSAGE_DLG_ICON_VERTIAL_MARGIN (theApp.DPI(12)) IMPLEMENT_DYNAMIC(CMessageDlg, CBaseDialog) CMessageDlg::CMessageDlg(CWnd* pParent /*=NULL*/) : CBaseDialog(IDD_MESSAGE_DIALOG, pParent) { } CMessageDlg::~CMessageDlg() { } void CMessageDlg::SetWindowTitle(LPCTSTR str) { m_title = str; } void CMessageDlg::SetInfoText(LPCTSTR str) { m_info = str; } void CMessageDlg::SetMessageText(LPCTSTR str) { m_message = str; } //void CMessageDlg::SetLinkInfo(LPCTSTR text, LPCTSTR url) //{ // m_link_text = text; // m_link_url = url; //} void CMessageDlg::SetMessageIcon(HICON hIcon) { m_icon = hIcon; } void CMessageDlg::SetStandarnMessageIcon(StandardIcon standard_icon) { HICON hIcon; PCWSTR icon{}; switch (standard_icon) { case SI_INFORMATION: icon = IDI_INFORMATION; break; case SI_WARNING: icon = IDI_WARNING; break; case SI_ERROR: icon = IDI_ERROR; break; } HRESULT hr = LoadIconWithScaleDown(NULL, icon, theApp.DPI(32), theApp.DPI(32), &hIcon); if (SUCCEEDED(hr)) SetMessageIcon(hIcon); } void CMessageDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_HELP_EDIT, m_message_edit); DDX_Control(pDX, IDC_INFO_STATIC, m_info_static); } BEGIN_MESSAGE_MAP(CMessageDlg, CBaseDialog) ON_WM_GETMINMAXINFO() //ON_NOTIFY(NM_CLICK, IDC_SYSLINK1, &CMessageDlg::OnNMClickSyslink1) ON_WM_PAINT() ON_WM_SIZE() END_MESSAGE_MAP() // CMessageDlg 消息处理程序 BOOL CMessageDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE); // 设置小图标 //获取初始时窗口的大小 CRect rect; GetWindowRect(rect); m_min_size.cx = rect.Width(); m_min_size.cy = rect.Height(); SetWindowText(m_title); m_info_static.SetWindowText(m_info); m_message_edit.SetWindowText(m_message); //CWnd* pLinkCtrl = GetDlgItem(IDC_SYSLINK1); //if (pLinkCtrl != nullptr) //{ // pLinkCtrl->ShowWindow(m_show_link_ctrl); // pLinkCtrl->SetWindowText(_T("") + m_link_text + _T("")); //} //设置图标的位置 if (m_icon != NULL) { CRect rc_info = GetControlRect(&m_info_static); //设置Static控件水平位置,为图标腾出空间 rc_info.left = rc_info.left + MESSAGE_DLG_ICON_SIZE + theApp.DPI(8); //设置Static控件的垂直位置 rc_info.MoveToY(rc_info.top + MESSAGE_DLG_ICON_VERTIAL_MARGIN); m_info_static.MoveWindow(rc_info); CRect rc_edit = GetControlRect(&m_message_edit); rc_edit.top += (MESSAGE_DLG_ICON_VERTIAL_MARGIN * 2); m_icon_pos.x = rc_edit.left; m_icon_pos.y = (rc_edit.top - MESSAGE_DLG_ICON_SIZE) / 2; m_message_edit.MoveWindow(rc_edit); } //没有图标,且消息标题为空时,隐藏Static控件 if (m_icon == NULL && m_info.IsEmpty()) { CRect rc_info = GetControlRect(&m_info_static); CRect rc_edit = GetControlRect(&m_message_edit); rc_edit.top = rc_info.top; m_message_edit.MoveWindow(rc_edit); m_info_static.ShowWindow(SW_HIDE); } return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CMessageDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //限制窗口最小大小 lpMMI->ptMinTrackSize.x = m_min_size.cx; //设置最小宽度 lpMMI->ptMinTrackSize.y = m_min_size.cy; //设置最小高度 CBaseDialog::OnGetMinMaxInfo(lpMMI); } //void CMessageDlg::OnNMClickSyslink1(NMHDR *pNMHDR, LRESULT *pResult) //{ // // TODO: 在此添加控件通知处理程序代码 // if(!m_link_url.IsEmpty()) // ShellExecute(NULL, _T("open"), m_link_url, NULL, NULL, SW_SHOW); //打开超链接 // // *pResult = 0; //} void CMessageDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CBaseDialog::OnPaint() CDrawCommon draw; draw.Create(&dc, this); draw.DrawIcon(m_icon, m_icon_pos, CSize(MESSAGE_DLG_ICON_SIZE, MESSAGE_DLG_ICON_SIZE)); } void CMessageDlg::OnSize(UINT nType, int cx, int cy) { CBaseDialog::OnSize(nType, cx, cy); if (m_info_static.GetSafeHwnd() != NULL && m_icon != NULL) { CRect rc_info = GetControlRect(&m_info_static); rc_info.right = cx; m_info_static.MoveWindow(rc_info); } if (m_message_edit.GetSafeHwnd() != NULL) { CRect rc_edit = GetControlRect(&m_message_edit); //设置Edit控件的垂直位置 CRect rc_ok = GetControlRect(IDOK); if (!rc_ok.IsRectEmpty()) { rc_edit.bottom = rc_ok.top - theApp.DPI(6); m_message_edit.MoveWindow(rc_edit); } } } ================================================ FILE: TrafficMonitor/MessageDlg.h ================================================ #pragma once #include "afxwin.h" #include "BaseDialog.h" // CMessageDlg 对话框 class CMessageDlg : public CBaseDialog { DECLARE_DYNAMIC(CMessageDlg) public: CMessageDlg(CWnd* pParent = NULL); // 标准构造函数 virtual ~CMessageDlg(); void SetWindowTitle(LPCTSTR str); //设置窗口标题 void SetInfoText(LPCTSTR str); //设置消息标题 void SetMessageText(LPCTSTR str); //设置消息文本 //void ShowLinkStatic(bool show = true) { m_show_link_ctrl = show; } //void SetLinkInfo(LPCTSTR text, LPCTSTR url); void SetMessageIcon(HICON hIcon); //消息对话框的系统标准图标 enum StandardIcon { SI_INFORMATION, SI_WARNING, SI_ERROR }; //为消息对话框框设置系统标准图标 void SetStandarnMessageIcon(StandardIcon standard_icon); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MESSAGE_DIALOG }; #endif protected: CEdit m_message_edit; CSize m_min_size; //窗口的最小大小 CStatic m_info_static; CString m_title; CString m_info; CString m_message; //CString m_link_text; //CString m_link_url; HICON m_icon{}; CPoint m_icon_pos{}; //图标的位置 //bool m_show_link_ctrl{ false }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI); //afx_msg void OnNMClickSyslink1(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnPaint(); afx_msg void OnSize(UINT nType, int cx, int cy); }; ================================================ FILE: TrafficMonitor/NetworkInfoDlg.cpp ================================================ // CNetworkInfoDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "NetworkInfoDlg.h" #include "afxdialogex.h" // CNetworkInfoDlg 对话框 IMPLEMENT_DYNAMIC(CNetworkInfoDlg, CBaseDialog) CNetworkInfoDlg::CNetworkInfoDlg(vector& adapters, MIB_IFROW* pIfRow, int connection_selected, CWnd* pParent /*=NULL*/) : CBaseDialog(IDD_NETWORK_INFO_DIALOG, pParent), m_connections(adapters), m_pIfRow(pIfRow), m_connection_selected(connection_selected) { m_current_connection = connection_selected; } CNetworkInfoDlg::~CNetworkInfoDlg() { } void CNetworkInfoDlg::ShowInfo() { CString temp; MIB_IFROW& network_info = GetConnectIfTable(m_connection_selected); //接口名 m_info_list.SetItemText(0, 1, network_info.wszName); //接口描述 m_info_list.SetItemText(1, 1, CCommon::StrToUnicode((const char*)network_info.bDescr).c_str()); //连接类型 switch (network_info.dwType) { case IF_TYPE_OTHER: temp = CCommon::LoadText(IDS_IF_TYPE_OTHER); break; case IF_TYPE_ETHERNET_CSMACD: temp = CCommon::LoadText(IDS_IF_TYPE_ETHERNET_CSMACD); break; case IF_TYPE_ISO88025_TOKENRING: temp = CCommon::LoadText(IDS_IF_TYPE_ISO88025_TOKENRING); break; case IF_TYPE_FDDI: temp = CCommon::LoadText(IDS_IF_TYPE_FDDI); break; case IF_TYPE_PPP: temp = CCommon::LoadText(IDS_IF_TYPE_PPP); break; case IF_TYPE_SOFTWARE_LOOPBACK: temp = CCommon::LoadText(IDS_IF_TYPE_SOFTWARE_LOOPBACK); break; case IF_TYPE_ATM: temp = CCommon::LoadText(IDS_IF_TYPE_ATM); break; case IF_TYPE_IEEE80211: temp = CCommon::LoadText(IDS_IF_TYPE_IEEE80211); break; case IF_TYPE_TUNNEL: temp = CCommon::LoadText(IDS_IF_TYPE_TUNNEL); break; case IF_TYPE_IEEE1394: temp = CCommon::LoadText(IDS_IF_TYPE_IEEE1394); break; case IF_TYPE_IEEE80216_WMAN: temp = CCommon::LoadText(IDS_IF_TYPE_IEEE80216_WMAN); break; case IF_TYPE_WWANPP: temp = CCommon::LoadText(IDS_IF_TYPE_WWANPP); break; case IF_TYPE_WWANPP2: temp = CCommon::LoadText(IDS_IF_TYPE_WWANPP2); break; default: temp = CCommon::LoadText(IDS_UNKNOW_CONNECTION); break; } m_info_list.SetItemText(2, 1, temp); //速度 temp.Format(_T("%dMbps"), network_info.dwSpeed / 1000000); m_info_list.SetItemText(3, 1, temp); //适配器物理地址 temp = _T(""); char buff[3]; for (size_t i{}; i < network_info.dwPhysAddrLen; i++) { sprintf_s(buff, "%.2x", network_info.bPhysAddr[i]); temp += buff; if (i != network_info.dwPhysAddrLen - 1) temp += _T('-'); } m_info_list.SetItemText(4, 1, temp); //IP地址 m_info_list.SetItemText(5, 1, GetConnection(m_connection_selected).ip_address.c_str()); //子网掩码 m_info_list.SetItemText(6, 1, GetConnection(m_connection_selected).subnet_mask.c_str()); //默认网关 m_info_list.SetItemText(7, 1, GetConnection(m_connection_selected).default_gateway.c_str()); //连接状态 switch (network_info.dwOperStatus) { case IF_OPER_STATUS_NON_OPERATIONAL: temp = CCommon::LoadText(IDS_IF_OPER_STATUS_NON_OPERATIONAL); break; case IF_OPER_STATUS_UNREACHABLE: temp = CCommon::LoadText(IDS_IF_OPER_STATUS_UNREACHABLE); break; case IF_OPER_STATUS_DISCONNECTED: temp = CCommon::LoadText(IDS_IF_OPER_STATUS_DISCONNECTED); break; case IF_OPER_STATUS_CONNECTING: temp = CCommon::LoadText(IDS_IF_OPER_STATUS_CONNECTING); break; case IF_OPER_STATUS_CONNECTED: temp = CCommon::LoadText(IDS_IF_OPER_STATUS_CONNECTED); break; case IF_OPER_STATUS_OPERATIONAL: temp = CCommon::LoadText(IDS_IF_OPER_STATUS_OPERATIONAL); break; //case IfOperStatusUp: // temp = CCommon::LoadText(IDS_IF_OPER_STATUS_UP); // break; //case IfOperStatusDown: //case IfOperStatusNotPresent: //case IfOperStatusLowerLayerDown: // temp = CCommon::LoadText(IDS_IF_OPER_STATUS_DOWN); // break; //case IfOperStatusTesting: //case IfOperStatusUnknown: // temp = CCommon::LoadText(IDS_UNKNOW_STATUS); // break; //case IfOperStatusDormant: // temp = CCommon::LoadText(IDS_IF_OPER_STATUS_DORMANT); //break; default: temp = CCommon::LoadText(IDS_UNKNOW_STATUS); break; } m_info_list.SetItemText(8, 1, temp); //已接收字节数 temp.Format(_T("%s (%s)"), CCommon::IntToString(network_info.dwInOctets, true, true), CCommon::DataSizeToString(network_info.dwInOctets)); m_info_list.SetItemText(9, 1, temp); //已发送字节数 temp.Format(_T("%s (%s)"), CCommon::IntToString(network_info.dwOutOctets, true, true), CCommon::DataSizeToString(network_info.dwOutOctets)); m_info_list.SetItemText(10, 1, temp); //自程序启动以来已接收字节数 unsigned __int64 in_bytes_since_start; in_bytes_since_start = network_info.dwInOctets - GetConnection(m_connection_selected).in_bytes; temp.Format(_T("%s (%s)"), CCommon::IntToString(in_bytes_since_start, true, true), CCommon::DataSizeToString(in_bytes_since_start)); m_info_list.SetItemText(11, 1, temp); //自程序启动以来已发送字节数 unsigned __int64 out_bytes_since_start; out_bytes_since_start = network_info.dwOutOctets - GetConnection(m_connection_selected).out_bytes; temp.Format(_T("%s (%s)"), CCommon::IntToString(out_bytes_since_start, true, true), CCommon::DataSizeToString(out_bytes_since_start)); m_info_list.SetItemText(12, 1, temp); //显示当前选择指示 CString str; str.Format(_T("%d/%d"), m_connection_selected + 1, m_connections.size()); SetDlgItemText(IDC_INDEX_STATIC, str); CFont* font = GetFont(); CWnd* index_static = GetDlgItem(IDC_INDEX_STATIC); if (m_current_connection == m_connection_selected && !theApp.m_cfg_data.m_select_all) index_static->SetFont(&m_font_bold); else index_static->SetFont(font); } void CNetworkInfoDlg::GetProgramElapsedTime() { //程序已运行时间 SYSTEMTIME current_time, time; GetLocalTime(¤t_time); time = CCommon::CompareSystemTime(current_time, m_start_time); CString temp; temp.Format(CCommon::LoadText(IDS_HOUR_MINUTE_SECOND), time.wHour, time.wMinute, time.wSecond); m_info_list.SetItemText(13, 1, temp); } MIB_IFROW& CNetworkInfoDlg::GetConnectIfTable(int connection_index) { static MIB_IFROW nouse{}; if (connection_index >= 0 && connection_index < static_cast(m_connections.size())) { int index = m_connections[connection_index].index; if (m_pIfRow != nullptr) return m_pIfRow[index]; } return nouse; } NetWorkConection CNetworkInfoDlg::GetConnection(int connection_index) { if (connection_index >= 0 && connection_index < static_cast(m_connections.size())) return m_connections[connection_index]; else return NetWorkConection(); } UINT CNetworkInfoDlg::GetInternetIPThreadFunc(LPVOID lpParam) { CCommon::SetThreadLanguage(theApp.m_general_data.language); //设置线程语言 CNetworkInfoDlg* p_instance = (CNetworkInfoDlg*)lpParam; wstring ip_address, ip_location; //IPV4 CCommon::GetInternetIp2(ip_address, ip_location, false); //获取外网IP地址, if (!IsWindow(p_instance->GetSafeHwnd())) //如果当前对话框已经销毁,则退出线程 return 0; if (!ip_address.empty()) { CString info; if (ip_location.empty()) info = ip_address.c_str(); else info.Format(_T("%s (%s)"), ip_address.c_str(), ip_location.c_str()); p_instance->m_info_list.SetItemText(14, 1, info); } else { p_instance->m_info_list.SetItemText(14, 1, CCommon::LoadText(IDS_GET_FAILED)); } //IPV6 wstring ipv6_address, ipv6_location; CCommon::GetInternetIp2(ip_address, ip_location, true); //获取外网IP地址, if (!IsWindow(p_instance->GetSafeHwnd())) //如果当前对话框已经销毁,则退出线程 return 0; if (!ip_address.empty()) { CString info; if (ip_location.empty()) info = ip_address.c_str(); else info.Format(_T("%s (%s)"), ip_address.c_str(), ip_location.c_str()); p_instance->m_info_list.SetItemText(15, 1, info); } else { p_instance->m_info_list.SetItemText(15, 1, CCommon::LoadText(IDS_GET_FAILED)); } p_instance->m_ip_acquired = true; return 0; } CString CNetworkInfoDlg::GetDialogName() const { return _T("NetworkInfoDlg"); } void CNetworkInfoDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_INFO_LIST1, m_info_list); } BEGIN_MESSAGE_MAP(CNetworkInfoDlg, CBaseDialog) ON_COMMAND(ID_COPY_TEXT, &CNetworkInfoDlg::OnCopyText) ON_NOTIFY(NM_RCLICK, IDC_INFO_LIST1, &CNetworkInfoDlg::OnNMRClickInfoList1) ON_WM_CLOSE() ON_BN_CLICKED(IDC_PREVIOUS_BUTTON, &CNetworkInfoDlg::OnBnClickedPreviousButton) ON_BN_CLICKED(IDC_NEXT_BUTTON, &CNetworkInfoDlg::OnBnClickedNextButton) ON_WM_TIMER() ON_WM_MOUSEWHEEL() ON_NOTIFY(NM_DBLCLK, IDC_INFO_LIST1, &CNetworkInfoDlg::OnNMDblclkInfoList1) END_MESSAGE_MAP() // CNetworkInfoDlg 消息处理程序 BOOL CNetworkInfoDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_INFO), FALSE); // 设置小图标 //重新获取IP地址 CAdapterCommon::RefreshIpAddress(m_connections); //初始化列表控件 CRect rect; m_info_list.GetClientRect(rect); m_info_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); int width0, width1; width0 = rect.Width() / 4; width1 = rect.Width() - width0 - theApp.DPI(20) - 1; m_info_list.InsertColumn(0, CCommon::LoadText(IDS_ITEM), LVCFMT_LEFT, width0); //插入第0列 m_info_list.InsertColumn(1, CCommon::LoadText(IDS_VALUE), LVCFMT_LEFT, width1); //插入第1列 //向列表中插入行 m_info_list.InsertItem(0, CCommon::LoadText(IDS_INTERFACE_NAME)); m_info_list.InsertItem(1, CCommon::LoadText(IDS_INTERFACE_DESCRIPTION)); m_info_list.InsertItem(2, CCommon::LoadText(IDS_CONNECTION_TYPE)); m_info_list.InsertItem(3, CCommon::LoadText(IDS_SPEED)); m_info_list.InsertItem(4, CCommon::LoadText(IDS_ADAPTER_PHYSICAL_ADDRESS)); m_info_list.InsertItem(5, CCommon::LoadText(IDS_IP_ADDRESS)); m_info_list.InsertItem(6, CCommon::LoadText(IDS_SUBNET_MASK)); m_info_list.InsertItem(7, CCommon::LoadText(IDS_DEFAULT_GATEWAY)); m_info_list.InsertItem(8, CCommon::LoadText(IDS_OPERATIONAL_STATUS)); m_info_list.InsertItem(9, CCommon::LoadText(IDS_BYTES_RECEIVED)); m_info_list.InsertItem(10, CCommon::LoadText(IDS_BYTES_SENT)); m_info_list.InsertItem(11, CCommon::LoadText(IDS_BYTES_RECEIVED_SINCE_START)); m_info_list.InsertItem(12, CCommon::LoadText(IDS_BYTES_SENT_SINCE_START)); m_info_list.InsertItem(13, CCommon::LoadText(IDS_PROGRAM_ELAPSED_TIME)); m_info_list.InsertItem(14, CCommon::LoadText(IDS_INTERNET_IP_ADDRESS, _T(" (ipv4)"))); m_info_list.InsertItem(15, CCommon::LoadText(IDS_INTERNET_IP_ADDRESS, _T(" (ipv6)"))); //if (theApp.m_cfg_data.m_show_internet_ip) //{ // m_info_list.SetItemText(14, 1, CCommon::LoadText(IDS_ACQUIRING, _T("..."))); // m_info_list.SetItemText(15, 1, CCommon::LoadText(IDS_ACQUIRING, _T("..."))); //} //else //{ m_info_list.SetItemText(14, 1, CCommon::LoadText(IDS_DOUBLE_CLICK_TO_ACQUIRE)); m_info_list.SetItemText(15, 1, CCommon::LoadText(IDS_DOUBLE_CLICK_TO_ACQUIRE)); //} //显示列表中的信息 LOGFONT lf{}; GetFont()->GetLogFont(&lf); lf.lfWeight = FW_BOLD; m_font_bold.CreateFontIndirect(&lf); ShowInfo(); GetProgramElapsedTime(); //CCommon::GetInternetIp(); //if (theApp.m_cfg_data.m_show_internet_ip) // m_pGetIPThread = AfxBeginThread(GetInternetIPThreadFunc, this); //启动获取外网IP的线程 //SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); //取消置顶 m_info_list.GetToolTips()->SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); CCommon::LoadMenuResource(m_menu, IDR_INFO_MENU); //装载右键菜单 SetTimer(CONNECTION_DETAIL_TIMER, 1000, NULL); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CNetworkInfoDlg::OnCopyText() { // TODO: 在此添加命令处理程序代码 if (!CCommon::CopyStringToClipboard(wstring(m_selected_string))) MessageBox(CCommon::LoadText(IDS_COPY_TO_CLIPBOARD_FAILED), NULL, MB_ICONWARNING); } void CNetworkInfoDlg::OnNMRClickInfoList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 //获取鼠标点击处的文本 int item, sub_item; item = pNMItemActivate->iItem; sub_item = pNMItemActivate->iSubItem; m_selected_string = m_info_list.GetItemText(item, sub_item); //弹出右键菜单 CMenu* pContextMenu = m_menu.GetSubMenu(0); //获取第一个弹出菜单 CPoint point1; //定义一个用于确定光标位置的位置 GetCursorPos(&point1); //获取当前光标的位置,以便使得菜单可以跟随光标 pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point1.x, point1.y, this); //在指定位置显示弹出菜单 *pResult = 0; } void CNetworkInfoDlg::OnClose() { // TODO: 在此添加消息处理程序代码和/或调用默认值 //对话框关闭时强制结束获取IP地址的线程 //if(theApp.m_cfg_data.m_show_internet_ip) // TerminateThread(m_pGetIPThread->m_hThread, 0); CBaseDialog::OnClose(); } void CNetworkInfoDlg::OnBnClickedPreviousButton() { // TODO: 在此添加控件通知处理程序代码 if (m_connections.size() > 1 && m_connection_selected > 0) { m_connection_selected--; ShowInfo(); } } void CNetworkInfoDlg::OnBnClickedNextButton() { // TODO: 在此添加控件通知处理程序代码 if (m_connections.size() > 1 && m_connection_selected < m_connections.size() - 1) { m_connection_selected++; ShowInfo(); } } BOOL CNetworkInfoDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_LEFT) { OnBnClickedPreviousButton(); return TRUE; } if (pMsg->wParam == VK_RIGHT) { OnBnClickedNextButton(); return TRUE; } } return CBaseDialog::PreTranslateMessage(pMsg); } void CNetworkInfoDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (nIDEvent == CONNECTION_DETAIL_TIMER) { GetProgramElapsedTime(); } CBaseDialog::OnTimer(nIDEvent); } BOOL CNetworkInfoDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //通过鼠标滚轮翻页 if (zDelta > 0) { OnBnClickedPreviousButton(); } if (zDelta < 0) { OnBnClickedNextButton(); } return CBaseDialog::OnMouseWheel(nFlags, zDelta, pt); } void CNetworkInfoDlg::OnNMDblclkInfoList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 if (/*!theApp.m_cfg_data.m_show_internet_ip && */!m_ip_acquired && (pNMItemActivate->iItem == 14 || pNMItemActivate->iItem == 15)) //双击了IP地址一行时 { m_info_list.SetItemText(14, 1, CCommon::LoadText(IDS_ACQUIRING, _T("..."))); m_info_list.SetItemText(15, 1, CCommon::LoadText(IDS_ACQUIRING, _T("..."))); m_pGetIPThread = AfxBeginThread(GetInternetIPThreadFunc, this); } *pResult = 0; } ================================================ FILE: TrafficMonitor/NetworkInfoDlg.h ================================================ #pragma once #include"Common.h" #include "afxcmn.h" #include "AdapterCommon.h" #include "BaseDialog.h" #include "ListCtrlEx.h" // CNetworkInfoDlg 对话框 class CNetworkInfoDlg : public CBaseDialog { DECLARE_DYNAMIC(CNetworkInfoDlg) public: CNetworkInfoDlg(vector& adapters, MIB_IFROW* pIfRow, int connection_selected, CWnd* pParent = NULL); // 标准构造函数 virtual ~CNetworkInfoDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_NETWORK_INFO_DIALOG }; #endif SYSTEMTIME m_start_time; //程序启动的时间 protected: vector& m_connections; MIB_IFROW* m_pIfRow; int m_connection_selected; //当前对话框显示的连接 int m_current_connection; //初始选择的连接 CListCtrlEx m_info_list; CMenu m_menu; CString m_selected_string; CFont m_font_bold; //默认字体的粗体 CWinThread* m_pGetIPThread; //获取外网IP的线程 bool m_ip_acquired{ false }; //如果已获取外网ip地址,则为true //void GetIPAddress(); //获取IP地址 void ShowInfo(); void GetProgramElapsedTime(); MIB_IFROW& GetConnectIfTable(int connection_index); //获取当前选择的网络连接的MIB_IFROW对象。connection_index为m_connections中的索引 NetWorkConection GetConnection(int connection_index); //获取当前选择的网络连接的NetWorkConection对象。connection_index为m_connections中的索引 //获取外网IP的线程函数 static UINT GetInternetIPThreadFunc(LPVOID lpParam); virtual CString GetDialogName() const override; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() virtual BOOL OnInitDialog(); public: afx_msg void OnCopyText(); afx_msg void OnNMRClickInfoList1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnClose(); afx_msg void OnBnClickedPreviousButton(); afx_msg void OnBnClickedNextButton(); virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); afx_msg void OnNMDblclkInfoList1(NMHDR* pNMHDR, LRESULT* pResult); }; ================================================ FILE: TrafficMonitor/Nullable.hpp ================================================ #pragma once #include #include #include #include #include template class AlignedStorage { private: alignas(T) std::byte m_buffer[sizeof(T)]{}; public: AlignedStorage() = default; ~AlignedStorage() = default; T& Get() noexcept { return *reinterpret_cast(m_buffer); }; const T& Get() const noexcept { return *reinterpret_cast(m_buffer); }; std::byte* operator&() noexcept { return m_buffer; }; }; // 皆不保证多线程操作安全 /** * @brief CNullable的默认删除器,注意重载operator()的参数为T* * * @tparam T 要删除的类型 */ template struct NullableDefaultDeleter { void operator()(T* p_this) { p_this->~T(); } }; /** * @brief 可空对象,允许对象在无默认构造函数的情况下仍然可以执行默认构造,代价是必须在使用对象之前执行Construct方法 * * @tparam T 实际存储的对象 * @tparam Deleter T的删除器 */ template > class CNullable { using StorageType = AlignedStorage; using RawType = std::decay_t; template static void EmplaceAt(RawType* p_object, Args&&... args) { ::new (p_object) RawType(std::forward(args)...); } public: CNullable(Deleter deleter = {}) : m_storage{deleter} {} ~CNullable() { if (m_has_value) { DestroySelf(); } } CNullable(const CNullable& other) : m_has_value{other.m_has_value}, m_storage{static_cast(other.m_storage)} { if (other) { EmplaceAt(m_storage.GetPointerUnsafe(), other.m_storage.GetUnsafe()); } } CNullable& operator=(const CNullable& other) { this->m_has_value = other.m_has_value; Deleter& ref_deleter = m_storage; ref_deleter = static_cast(other.m_storage); if (other) { EmplaceAt(m_storage.GetPointerUnsafe(), other.m_storage.GetUnsafe()); } } CNullable(CNullable&& other) noexcept : m_has_value{other.m_has_value}, m_storage{std::move(static_cast(other.m_storage))} { if (other) { EmplaceAt(m_storage.GetPointerUnsafe(), std::move(other.m_storage.GetUnsafe())); } } CNullable& operator=(CNullable&& other) noexcept { this->m_has_value = other.m_has_value; Deleter& ref_deleter = m_storage; ref_deleter = std::move(static_cast(other.m_storage)); if (other) { EmplaceAt(m_storage.GetPointerUnsafe(), std::move(other.m_storage.GetUnsafe())); } } template void Construct(Args&&... args) { if (m_has_value) { DestroySelf(); m_has_value = false; } EmplaceAt(m_storage.GetPointerUnsafe(), std::forward(args)...); m_has_value = true; } const T& GetUnsafe() const noexcept { return m_storage.GetUnsafe(); } T& GetUnsafe() noexcept { return m_storage.GetUnsafe(); } const T& Get() const noexcept { Check(); return GetUnsafe(); } T& Get() noexcept { Check(); return GetUnsafe(); } bool HasValue() const noexcept { return m_has_value; } operator bool() const noexcept { return HasValue(); } private: void DestroySelf() { auto&& ref_deleter = static_cast(m_storage); ref_deleter(m_storage.GetPointerUnsafe()); } void Check() const { if (!m_has_value) { throw CallNullObjectError{}; } } class CallNullObjectError : public std::runtime_error { public: CallNullObjectError() : std::runtime_error{"Value is uninitialized!"} {} ~CallNullObjectError() override = default; }; class StorageAndEboDeleter : public Deleter { private: StorageType m_storage; public: explicit StorageAndEboDeleter(Deleter deleter) : Deleter{deleter} {} ~StorageAndEboDeleter() = default; StorageAndEboDeleter(const StorageAndEboDeleter&) = delete; StorageAndEboDeleter& operator=(const StorageAndEboDeleter&) = delete; const T& GetUnsafe() const noexcept { return *static_cast(static_cast(&m_storage)); } T& GetUnsafe() noexcept { return *static_cast(static_cast(&m_storage)); } const T* GetPointerUnsafe() const noexcept { return static_cast(static_cast(&m_storage)); } T* GetPointerUnsafe() noexcept { return static_cast(static_cast(&m_storage)); } }; bool m_has_value{false}; StorageAndEboDeleter m_storage; }; template > auto MakeNullableObject(Deleter deleter) -> CNullable { return {deleter}; } /** * @brief 可延迟构造的对象,用于预先分配内存,在有使用该对象的请求时立即构造此对象,对象必须可默认构造 * * @tparam T 要被应用这一特性的类型 * @tparam Deleter T的删除器,默认为NullableDefaultDeleter */ template > class CLazyConstructable { public: CLazyConstructable() = default; ~CLazyConstructable() = default; T& Get() { if (m_content) { return m_content.GetUnsafe(); } else { m_content.Construct(); return m_content.GetUnsafe(); } } private: CNullable m_content{}; }; template class CLazyConstructableWithInitializer; template class Container, class... InitArgs> class CLazyConstructableWithInitializer> { private: using ArgsContainer = Container; using ArgsInitFunction = std::function; constexpr static std::size_t init_args_size = sizeof...(InitArgs); template void ConstructHelper(Tuple&& args, std::index_sequence) { m_content.Construct(std::get(args)...); } public: CLazyConstructableWithInitializer(ArgsInitFunction init_function) : m_init_function{init_function} { } ~CLazyConstructableWithInitializer() = default; T& Get() { if (m_content) { return m_content.GetUnsafe(); } else { auto init_args{std::move(m_init_function())}; ConstructHelper( init_args, std::make_index_sequence{}>{}); return m_content.GetUnsafe(); } } bool HasValue() const noexcept { return m_content.HasValue(); } operator bool() const noexcept { return HasValue(); } private: CNullable m_content{}; ArgsInitFunction m_init_function{}; }; template using DefaultCLazyConstructableWithInitializer = CLazyConstructableWithInitializer, std::tuple>; ================================================ FILE: TrafficMonitor/OptionsDlg.cpp ================================================ // OptionsDlg.cpp : 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "OptionsDlg.h" #include "afxdialogex.h" // COptionsDlg 对话框 IMPLEMENT_DYNAMIC(COptionsDlg, CBaseDialog) COptionsDlg::COptionsDlg(int tab, CWnd* pParent /*=NULL*/) : CBaseDialog(IDD_OPTIONS_DIALOG, pParent), m_tab_selected{ tab } { } COptionsDlg::~COptionsDlg() { } CString COptionsDlg::GetDialogName() const { return OPTION_DLG_NAME; } void COptionsDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_TAB1, m_tab); } BEGIN_MESSAGE_MAP(COptionsDlg, CBaseDialog) ON_WM_SIZE() ON_BN_CLICKED(IDC_APPLY_BUTTON, &COptionsDlg::OnBnClickedApplyButton) END_MESSAGE_MAP() // COptionsDlg 消息处理程序 BOOL COptionsDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_SETTINGS), FALSE); // 设置小图标 //创建子对话框 m_tab1_dlg.Create(IDD_MAIN_WND_SETTINGS_DIALOG, &m_tab); m_tab2_dlg.Create(IDD_TASKBAR_SETTINGS_DIALOG, &m_tab); m_tab3_dlg.Create(IDD_GENERAL_SETTINGS_DIALOG, &m_tab); //保存子对话框 m_tab_vect.push_back(&m_tab1_dlg); m_tab_vect.push_back(&m_tab2_dlg); m_tab_vect.push_back(&m_tab3_dlg); //获取子对话框的初始高度 for (const auto* pDlg : m_tab_vect) { CRect rect; pDlg->GetWindowRect(rect); m_tab_height.push_back(rect.Height()); } //添加对话框 m_tab.AddWindow(&m_tab1_dlg, CCommon::LoadText(IDS_MAIN_WINDOW_SETTINGS)); m_tab.AddWindow(&m_tab2_dlg, CCommon::LoadText(IDS_TASKBAR_WINDOW_SETTINGS)); m_tab.AddWindow(&m_tab3_dlg, CCommon::LoadText(IDS_GENERAL_SETTINGS)); //为每个标签添加图标 CImageList ImageList; ImageList.Create(theApp.DPI(16), theApp.DPI(16), ILC_COLOR32 | ILC_MASK, 2, 2); ImageList.Add(theApp.GetMenuIcon(IDI_MAIN_WINDOW)); ImageList.Add(theApp.GetMenuIcon(IDI_TASKBAR_WINDOW)); ImageList.Add(theApp.GetMenuIcon(IDI_SETTINGS)); m_tab.SetImageList(&ImageList); ImageList.Detach(); m_tab.SetItemSize(CSize(theApp.DPI(60), theApp.DPI(24))); m_tab.AdjustTabWindowSize(); //为每个子窗口设置滚动信息 for (size_t i = 0; i < m_tab_vect.size(); i++) { m_tab_vect[i]->SetScrollbarInfo(m_tab.m_tab_rect.Height(), m_tab_height[i]); } //设置默认选中的标签 if (m_tab_selected < 0 || m_tab_selected >= m_tab.GetItemCount()) m_tab_selected = 0; m_tab.SetCurTab(m_tab_selected); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void COptionsDlg::OnOK() { // TODO: 在此添加专用代码和/或调用基类 m_tab1_dlg.OnOK(); m_tab2_dlg.OnOK(); m_tab3_dlg.OnOK(); CBaseDialog::OnOK(); } void COptionsDlg::OnSize(UINT nType, int cx, int cy) { CBaseDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 if (nType != SIZE_MINIMIZED) { //为每个子窗口设置滚动信息 for (size_t i = 0; i < m_tab_vect.size(); i++) { m_tab_vect[i]->SetScrollbarInfo(m_tab.m_tab_rect.Height(), m_tab_height[i]); } } } void COptionsDlg::OnCancel() { // TODO: 在此添加专用代码和/或调用基类 m_tab3_dlg.OnCancel(); CBaseDialog::OnCancel(); } void COptionsDlg::OnBnClickedApplyButton() { m_tab2_dlg.SaveColorSettingToDefaultStyle(); ::SendMessage(theApp.m_pMainWnd->GetSafeHwnd(), WM_SETTINGS_APPLIED, (WPARAM)this, 0); for (size_t i = 0; i < m_tab_vect.size(); i++) { m_tab_vect[i]->OnSettingsApplied(); } } ================================================ FILE: TrafficMonitor/OptionsDlg.h ================================================ #pragma once #include "MainWndSettingsDlg.h" #include "TaskBarSettingsDlg.h" #include "GeneralSettingsDlg.h" #include "afxcmn.h" #include "CTabCtrlEx.h" #include "BaseDialog.h" // COptionsDlg 对话框 #define OPTION_DLG_NAME _T("OptionsDlg") class COptionsDlg : public CBaseDialog { DECLARE_DYNAMIC(COptionsDlg) public: COptionsDlg(int tab = 0, CWnd* pParent = NULL); // 标准构造函数 virtual ~COptionsDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_OPTIONS_DIALOG }; #endif CMainWndSettingsDlg m_tab1_dlg{ this }; CTaskBarSettingsDlg m_tab2_dlg{ this }; CGeneralSettingsDlg m_tab3_dlg{ this }; protected: CTabCtrlEx m_tab; int m_tab_selected; std::vector m_tab_vect; std::vector m_tab_height; virtual CString GetDialogName() const override; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); virtual void OnOK(); afx_msg void OnSize(UINT nType, int cx, int cy); virtual void OnCancel(); afx_msg void OnBnClickedApplyButton(); }; ================================================ FILE: TrafficMonitor/PdhHardwareQuery/CPUUsage.cpp ================================================ #include "stdafx.h" #include "CPUUsage.h" #include "Common.h" #include "TrafficMonitor.h" #include #include /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// CPdhCPUUsage::CPdhCPUUsage() : CPdhQuery(theApp.m_win_version.GetMajorVersion() >= 10 ? _T("\\Processor Information(_Total)\\% Processor Utility") : _T("\\Processor Information(_Total)\\% Processor Time")) { } bool CPdhCPUUsage::GetCPUUsage(int& cpu_usage) { double value{}; if (QueryValue(value)) { cpu_usage = static_cast(value); if (cpu_usage > 100) cpu_usage = 100; return true; } return false; } /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// CCPUUsage::CCPUUsage() { } void CCPUUsage::SetUseCPUTimes(bool use_get_system_times) { if (m_use_get_system_times != use_get_system_times) { m_use_get_system_times = use_get_system_times; } } int CCPUUsage::GetCpuUsage() { int cpu_usage{}; if (m_use_get_system_times) { cpu_usage = GetCpuUsageByGetSystemTimes(); } else { //如果通过pdh获取CPU利用率失败,采用GetSystemTimes获取 if (!m_pdh_cup_usage_query.GetCPUUsage(cpu_usage)) { cpu_usage = GetCpuUsageByGetSystemTimes(); //写入日志 //static bool write_log = false; //if (!write_log) //{ // CString str_log = CCommon::LoadTextFormat(IDS_GET_CPU_USAGE_BY_PDH_FAILED_LOG, { fullCounterPath }); // CCommon::WriteLog(str_log, theApp.m_log_path.c_str()); // write_log = true; //} } } return cpu_usage; } int CCPUUsage::GetCpuUsageByGetSystemTimes() { int cpu_usage{}; FILETIME idleTime; FILETIME kernelTime; FILETIME userTime; GetSystemTimes(&idleTime, &kernelTime, &userTime); __int64 idle = CCommon::CompareFileTime2(m_preidleTime, idleTime); __int64 kernel = CCommon::CompareFileTime2(m_prekernelTime, kernelTime); __int64 user = CCommon::CompareFileTime2(m_preuserTime, userTime); if (kernel + user == 0) { cpu_usage = 0; } else { //(总的时间-空闲时间)/总的时间=占用cpu的时间就是使用率 cpu_usage = static_cast(abs((kernel + user - idle) * 100 / (kernel + user))); } m_preidleTime = idleTime; m_prekernelTime = kernelTime; m_preuserTime = userTime; return cpu_usage; } ================================================ FILE: TrafficMonitor/PdhHardwareQuery/CPUUsage.h ================================================ #pragma once #include #include #include "PdhQuery.h" class CPdhCPUUsage : public CPdhQuery { public: CPdhCPUUsage(); ~CPdhCPUUsage() { } bool GetCPUUsage(int& cpu_usage); }; ////////////////////////////////////////////////////////////////////////////////// class CCPUUsage { public: CCPUUsage(); ~CCPUUsage() {} void SetUseCPUTimes(bool use_get_system_times); //设置获取CPU利用率的方式,是通过GetSystemTimes还是Pdh int GetCpuUsage(); private: int GetCpuUsageByGetSystemTimes(); private: bool m_use_get_system_times{ true }; //是否使用GetSysTime这个API来获取CPU利用率 FILETIME m_preidleTime{}; FILETIME m_prekernelTime{}; FILETIME m_preuserTime{}; CPdhCPUUsage m_pdh_cup_usage_query; }; ================================================ FILE: TrafficMonitor/PdhHardwareQuery/CpuFreq.cpp ================================================ #include "stdafx.h" #include "CpuFreq.h" #include #include /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// typedef struct _PROCESSOR_POWER_INFORMATION { ULONG Number; ULONG MaxMhz; ULONG CurrentMhz; ULONG MhzLimit; ULONG MaxIdleState; ULONG CurrentIdleState; } PROCESSOR_POWER_INFORMATION, * PPROCESSOR_POWER_INFORMATION; CPdhCpuFreq::CPdhCpuFreq() : CPdhQuery(_T("\\Processor Information(_Total)\\% Processor Performance")) { //获取max_cpu_freq SYSTEM_INFO si; GetSystemInfo(&si); auto ppInfo = std::vector(si.dwNumberOfProcessors); auto status = CallNtPowerInformation(POWER_INFORMATION_LEVEL::ProcessorInformation, NULL, 0, &ppInfo[0], sizeof(PROCESSOR_POWER_INFORMATION) * ppInfo.size()); for (size_t i = 0; i < ppInfo.size(); i++) { max_cpu_freq = max(max_cpu_freq, ppInfo[i].MaxMhz / 1000.f); } } bool CPdhCpuFreq::GetCpuFreq(float& freq) { double value{}; if (QueryValue(value)) { freq = value / 100 * max_cpu_freq; return true; } return false; } ================================================ FILE: TrafficMonitor/PdhHardwareQuery/CpuFreq.h ================================================ #pragma once #include #include #include "PdhQuery.h" class CPdhCpuFreq : public CPdhQuery { public: CPdhCpuFreq(); // 调用此函数获取CPU频率。 // 注意:第二次调用此函数开始才能获取到值,两次调用的时间不应过短,最好大于200毫秒 bool GetCpuFreq(float& freq); private: float max_cpu_freq = 0; }; ================================================ FILE: TrafficMonitor/PdhHardwareQuery/DiskUsage.cpp ================================================ #include "stdafx.h" #include "DiskUsage.h" #include #include CPdhDiskUsage::CPdhDiskUsage() : CPdhQuery(_T("\\PhysicalDisk(*)\\% Idle Time")) { m_isAvailable = Initialize(); if (m_isAvailable) { // 预热:触发PDH内部初始化(首次QueryValues会填充实例列表) std::vector dummy; QueryValues(dummy); ExtractDiskNames(); } } CPdhDiskUsage::~CPdhDiskUsage() { } void CPdhDiskUsage::ExtractDiskNames() { m_diskNames.clear(); std::vector values; if (!QueryValues(values)) return; for (const auto& item : values) { CString name(item.name.c_str()); m_diskNames.push_back(name); } } int CPdhDiskUsage::CalculateUtilization(double idleTime) const { // 关键处理:NVMe/RAID等多队列磁盘的空闲时间可能 >100% // 例如: 4队列磁盘空闲时间=400% → 实际空闲=100% → 利用率=0% if (idleTime > 100.0) idleTime = 100.0; // 确保范围在0-100 idleTime = (std::max)(0.0, (std::min)(100.0, idleTime)); // 利用率 = 100% - 空闲时间 double utilization = 100.0 - idleTime; return static_cast(utilization + 0.5); // 四舍五入 } bool CPdhDiskUsage::GetDiskUsage(int diskIndex, int& usage) { usage = 0; if (!m_isAvailable) return false; std::vector values; if (!QueryValues(values) || values.empty()) return false; if (diskIndex >= 0 && diskIndex < static_cast(values.size())) { usage = CalculateUtilization(values[diskIndex].value); return true; } return false; } int CPdhDiskUsage::FindDiskIndex(const std::wstring diskName) { int disk_index = -1; for (int i = 0; i < static_cast(m_diskNames.size()); i++) { if (diskName == m_diskNames[i].GetString()) { disk_index = i; break; } } return disk_index; } ================================================ FILE: TrafficMonitor/PdhHardwareQuery/DiskUsage.h ================================================ #pragma once #include "PdhQuery.h" class CPdhDiskUsage : public CPdhQuery { public: CPdhDiskUsage(); ~CPdhDiskUsage(); // 获取指定物理磁盘的利用率(0-100) // diskIndex: 0=第一个物理磁盘, 1=第二个... // 返回值: true=成功, false=失败(首次调用通常失败,需间隔200ms+重试) bool GetDiskUsage(int diskIndex, /*out*/ int& usage); // 获取物理磁盘名称列表 const std::vector& GetDiskNames() const { return m_diskNames; } //根据名称查找一个硬盘的序号 int FindDiskIndex(const std::wstring diskName); // 检查磁盘监控是否可用 bool IsAvailable() const { return m_isAvailable; } private: // 从计数器实例提取磁盘名称 void ExtractDiskNames(); // 处理空闲时间 int CalculateUtilization(double idleTime) const; private: bool m_isAvailable{ false }; std::vector m_diskNames; // 磁盘名称列表,按PDH返回顺序 }; ================================================ FILE: TrafficMonitor/PdhHardwareQuery/GpuUsage.cpp ================================================ #include "stdafx.h" #include "GpuUsage.h" /////////////////////////////////////////////////////////////////////////////////////////// // CPdhGPUUsage 实现 /////////////////////////////////////////////////////////////////////////////////////////// CPdhGPUUsage::CPdhGPUUsage() : CPdhQuery(_T("\\GPU Engine(*)\\Utilization Percentage")) { } CPdhGPUUsage::~CPdhGPUUsage() { } bool CPdhGPUUsage::GetGpuUsage(int& usage) { if (isInitialized) { std::vector valueItems; if (QueryValues(valueItems)) { if (!valueItems.empty()) { //获取所有类型的利用率 std::map gpu_usage_map; for (const auto& item : valueItems) { std::wstring item_name = item.name; size_t index = item.name.rfind(L'_'); if (index != std::wstring::npos) item_name = item.name.substr(index + 1); gpu_usage_map[item_name] += item.value; } //查找所有类型中最大的值作为总利用率(同Windows任务管理器的处理) double max_value = 0; for (const auto& item : gpu_usage_map) { if (item.second > max_value) max_value = item.second; } usage = static_cast(max_value + 0.5); // 四舍五入 usage = min(max(usage, 0), 100); // 限制在0-100范围 return true; } } } return false; } ================================================ FILE: TrafficMonitor/PdhHardwareQuery/GpuUsage.h ================================================ #pragma once #include "PdhQuery.h" // GPU利用率监控类 class CPdhGPUUsage : public CPdhQuery { public: CPdhGPUUsage(); ~CPdhGPUUsage(); // 获取GPU利用率(0-100) // 返回值: true=成功, false=失败 // 注意:首次调用通常返回false(PDH需要预热),建议间隔200ms以上再次调用 bool GetGpuUsage(/*out*/ int& usage); }; ================================================ FILE: TrafficMonitor/PdhHardwareQuery/PdhQuery.cpp ================================================ #include "stdafx.h" #include "PdhQuery.h" CPdhQuery::CPdhQuery(LPCTSTR _fullCounterPath) : fullCounterPath(_fullCounterPath) { Initialize(); } CPdhQuery::~CPdhQuery() { //رղѯ PdhCloseQuery(query); } bool CPdhQuery::Initialize() { if (isInitialized) return true; PDH_STATUS status; //򿪲ѯ status = PdhOpenQuery(NULL, NULL, &query); if (status != ERROR_SUCCESS) return false; //Ӽ status = PdhAddCounter(query, fullCounterPath.GetString(), NULL, &counter); //ȵPdhAddCounterʧʹPdhAddEnglishCounterһ if (status != ERROR_SUCCESS) { status = PdhAddEnglishCounter(query, fullCounterPath.GetString(), NULL, &counter); if (status != ERROR_SUCCESS) { PdhCloseQuery(query); query = nullptr; return false; } } //ʼ PdhCollectQueryData(query); isInitialized = true; return true; } bool CPdhQuery::QueryValue(double& value) { if (!isInitialized) return false; // PdhCollectQueryData(query); PDH_FMT_COUNTERVALUE pdhValue; DWORD dwValue; PDH_STATUS status = PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, &dwValue, &pdhValue); if (status != ERROR_SUCCESS) { return false; } value = pdhValue.doubleValue; return true; } bool CPdhQuery::QueryValues(std::vector& values) { values.clear(); if (!isInitialized) return false; // PdhCollectQueryData(query); DWORD dwBufferSize = 0; // Size of the pItems buffer DWORD dwItemCount = 0; // Number of items in the pItems buffer PDH_FMT_COUNTERVALUE_ITEM* pItems = NULL; PDH_STATUS status = PdhGetFormattedCounterArray(counter, PDH_FMT_DOUBLE, &dwBufferSize, &dwItemCount, pItems); if (PDH_MORE_DATA == status) { pItems = (PDH_FMT_COUNTERVALUE_ITEM*)malloc(dwBufferSize); if (pItems) { status = PdhGetFormattedCounterArray(counter, PDH_FMT_DOUBLE, &dwBufferSize, &dwItemCount, pItems); if (ERROR_SUCCESS == status) { // Loop through the array and print the instance name and counter value. for (DWORD i = 0; i < dwItemCount; i++) { CounterValueItem value_item; value_item.name = pItems[i].szName; value_item.value = pItems[i].FmtValue.doubleValue; values.push_back(value_item); } } else { return false; } free(pItems); pItems = NULL; dwBufferSize = dwItemCount = 0; } else { return false; } } else { return false; } return true; } ================================================ FILE: TrafficMonitor/PdhHardwareQuery/PdhQuery.h ================================================ #pragma once #include #include class CPdhQuery { public: CPdhQuery(LPCTSTR _fullCounterPath); virtual ~CPdhQuery(); protected: bool Initialize(); bool QueryValue(double& value); struct CounterValueItem { std::wstring name; double value{}; }; bool QueryValues(std::vector& values); protected: HQUERY query = nullptr; HCOUNTER counter = nullptr; bool isInitialized = false; CString fullCounterPath; }; ================================================ FILE: TrafficMonitor/PictureStatic.cpp ================================================ #include "stdafx.h" #include "PictureStatic.h" CPictureStatic::CPictureStatic() { } CPictureStatic::~CPictureStatic() { m_memDC.DeleteDC(); m_bitmap.Detach(); } void CPictureStatic::SetPicture(UINT pic_id) { m_memDC.DeleteDC(); if (!m_bitmap.LoadBitmap(pic_id)) //λͼ return; //ȡͼʵʴС GetObject(m_bitmap, sizeof(BITMAP), &m_bm); CDC* pDC = GetDC(); m_memDC.CreateCompatibleDC(pDC); m_memDC.SelectObject(&m_bitmap); //ȡؼС GetClientRect(m_rect); //ֶػ Invalidate(); } void CPictureStatic::SetPicture(HBITMAP hBitmap) { m_memDC.DeleteDC(); m_bitmap.Detach(); if (!m_bitmap.Attach(hBitmap)) return; //ȡͼʵʴС GetObject(m_bitmap, sizeof(BITMAP), &m_bm); CDC* pDC = GetDC(); m_memDC.CreateCompatibleDC(pDC); m_memDC.SelectObject(&m_bitmap); //ȡؼС GetClientRect(m_rect); //ֶػ Invalidate(); } BEGIN_MESSAGE_MAP(CPictureStatic, CStatic) ON_WM_PAINT() END_MESSAGE_MAP() void CPictureStatic::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: ڴ˴Ϣ // ΪͼϢ CStatic::OnPaint() if (m_bitmap.m_hObject != NULL) { // бͼƬʧ dc.SetStretchBltMode(HALFTONE); dc.SetBrushOrg(0, 0); //ƽڴDCеͼ dc.StretchBlt(0, 0, m_rect.Width(), m_rect.Height(), &m_memDC, 0, 0, m_bm.bmWidth, m_bm.bmHeight, SRCCOPY); //򸸴ڷػϢ CWnd* pParent{ GetParent() }; if (pParent != nullptr) pParent->SendMessage(WM_CONTROL_REPAINT, (WPARAM)this, LPARAM(&dc)); } } ================================================ FILE: TrafficMonitor/PictureStatic.h ================================================ /*һʾͼƬľ̬ؼ ҪʱSetPicture()þ̬ͼƬ ؼػʱ򸸴ڷWM_CONTROL_REPAINTϢ ͨwParamݵǰؼCWndָ룬ͨlParamCDCָ */ #pragma once #define WM_CONTROL_REPAINT (WM_USER + 1003) //ؼػϢ class CPictureStatic : public CStatic { public: CPictureStatic(); ~CPictureStatic(); void SetPicture(UINT pic_id); void SetPicture(HBITMAP hBitmap); protected: CDC m_memDC; CBitmap m_bitmap; CRect m_rect; BITMAP m_bm; public: DECLARE_MESSAGE_MAP() afx_msg void OnPaint(); }; ================================================ FILE: TrafficMonitor/PluginInfoDlg.cpp ================================================ // PluginInfoDlg.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "PluginInfoDlg.h" #include "FilePathHelper.h" // CPluginInfoDlg 对话框 IMPLEMENT_DYNAMIC(CPluginInfoDlg, CBaseDialog) CPluginInfoDlg::CPluginInfoDlg(int plugin_index, CWnd* pParent /*=nullptr*/) : CBaseDialog(IDD_NETWORK_INFO_DIALOG, pParent), m_cur_index(plugin_index) { if (m_cur_index < 0 || m_cur_index >= static_cast(theApp.m_plugins.GetPlugins().size())) m_cur_index = 0; } CPluginInfoDlg::~CPluginInfoDlg() { } void CPluginInfoDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_INFO_LIST1, m_info_list); } void CPluginInfoDlg::ShowInfo() { if (m_cur_index >= 0 && m_cur_index < static_cast(theApp.m_plugins.GetPlugins().size())) { auto& plugin_info = theApp.m_plugins.GetPlugins()[m_cur_index]; m_info_list.SetItemText(RI_NAME, 1, plugin_info.Property(ITMPlugin::TMI_NAME).c_str()); m_info_list.SetItemText(RI_DESCRIPTION, 1, plugin_info.Property(ITMPlugin::TMI_DESCRIPTION).c_str()); m_info_list.SetItemText(RI_FILE_NAME, 1, CFilePathHelper(plugin_info.file_path).GetFileName().c_str()); m_info_list.SetItemText(RI_FILE_PATH, 1, plugin_info.file_path.c_str()); m_info_list.SetItemText(RI_ITEM_NUM, 1, std::to_wstring(plugin_info.plugin_items.size()).c_str()); wstring item_names; wstring item_id; for (const auto& item : plugin_info.plugin_items) { item_names += item->GetItemName(); item_names += L";"; item_id += item->GetItemId(); item_id += L";"; } if (!plugin_info.plugin_items.empty()) { item_names.pop_back(); item_id.pop_back(); } m_info_list.SetItemText(RI_ITEM_NAMES, 1, item_names.c_str()); m_info_list.SetItemText(RI_ITEM_ID, 1, item_id.c_str()); m_info_list.SetItemText(RI_AUTHOR, 1, plugin_info.Property(ITMPlugin::TMI_AUTHOR).c_str()); m_info_list.SetItemText(RI_COPYRIGHT, 1, plugin_info.Property(ITMPlugin::TMI_COPYRIGHT).c_str()); m_info_list.SetItemText(RI_URL, 1, plugin_info.Property(ITMPlugin::TMI_URL).c_str()); m_info_list.SetItemText(RI_VERSION, 1, plugin_info.Property(ITMPlugin::TMI_VERSION).c_str()); std::wstring api_version; if (plugin_info.plugin != nullptr) api_version = std::to_wstring(plugin_info.plugin->GetAPIVersion()); m_info_list.SetItemText(RI_API_VERSION, 1, api_version.c_str()); } //显示当前选择指示 CString str; str.Format(_T("%d/%d"), m_cur_index + 1, theApp.m_plugins.GetPlugins().size()); SetDlgItemText(IDC_INDEX_STATIC, str); } BEGIN_MESSAGE_MAP(CPluginInfoDlg, CBaseDialog) ON_COMMAND(ID_COPY_TEXT, &CPluginInfoDlg::OnCopyText) ON_NOTIFY(NM_RCLICK, IDC_INFO_LIST1, &CPluginInfoDlg::OnNMRClickInfoList1) ON_BN_CLICKED(IDC_PREVIOUS_BUTTON, &CPluginInfoDlg::OnBnClickedPreviousButton) ON_BN_CLICKED(IDC_NEXT_BUTTON, &CPluginInfoDlg::OnBnClickedNextButton) ON_WM_MOUSEWHEEL() END_MESSAGE_MAP() // CPluginInfoDlg 消息处理程序 BOOL CPluginInfoDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetWindowText(CCommon::LoadText(IDS_PLUGIN_INFO)); SetIcon(theApp.GetMenuIcon(IDI_PLUGINS), FALSE); // 设置小图标 //初始化列表控件 CRect rect; m_info_list.GetClientRect(rect); m_info_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); int width0, width1; width0 = rect.Width() / 4; width1 = rect.Width() - width0 - theApp.DPI(20) - 1; m_info_list.InsertColumn(0, CCommon::LoadText(IDS_ITEM), LVCFMT_LEFT, width0); m_info_list.InsertColumn(1, CCommon::LoadText(IDS_VALUE), LVCFMT_LEFT, width1); //向列表中插入行 for (int i = 0; i < RI_MAX; i++) { m_info_list.InsertItem(i, GetRowName(i)); } //显示列表中的信息 ShowInfo(); m_info_list.GetToolTips()->SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); CCommon::LoadMenuResource(m_menu, IDR_INFO_MENU); //装载右键菜单 return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } CString CPluginInfoDlg::GetDialogName() const { return _T("PluginInfoDlg"); } void CPluginInfoDlg::OnCopyText() { if (!CCommon::CopyStringToClipboard(wstring(m_selected_string))) MessageBox(CCommon::LoadText(IDS_COPY_TO_CLIPBOARD_FAILED), NULL, MB_ICONWARNING); } void CPluginInfoDlg::OnNMRClickInfoList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 //获取鼠标点击处的文本 int item, sub_item; item = pNMItemActivate->iItem; sub_item = pNMItemActivate->iSubItem; m_selected_string = m_info_list.GetItemText(item, sub_item); //弹出右键菜单 CMenu* pContextMenu = m_menu.GetSubMenu(0); //获取第一个弹出菜单 CPoint point1; //定义一个用于确定光标位置的位置 GetCursorPos(&point1); //获取当前光标的位置,以便使得菜单可以跟随光标 pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point1.x, point1.y, this); //在指定位置显示弹出菜单 *pResult = 0; } void CPluginInfoDlg::OnBnClickedPreviousButton() { if (m_cur_index > 0) { m_cur_index--; ShowInfo(); } } void CPluginInfoDlg::OnBnClickedNextButton() { if (m_cur_index < static_cast(theApp.m_plugins.GetPlugins().size() - 1)) { m_cur_index++; ShowInfo(); } } BOOL CPluginInfoDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_LEFT) { OnBnClickedPreviousButton(); return TRUE; } if (pMsg->wParam == VK_RIGHT) { OnBnClickedNextButton(); return TRUE; } } return CBaseDialog::PreTranslateMessage(pMsg); } BOOL CPluginInfoDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { //通过鼠标滚轮翻页 if (zDelta > 0) { OnBnClickedPreviousButton(); } if (zDelta < 0) { OnBnClickedNextButton(); } return CBaseDialog::OnMouseWheel(nFlags, zDelta, pt); } CString CPluginInfoDlg::GetRowName(int row_index) { switch (row_index) { case CPluginInfoDlg::RI_NAME: return CCommon::LoadText(IDS_NAME); case CPluginInfoDlg::RI_DESCRIPTION: return CCommon::LoadText(IDS_DESCRIPTION); case CPluginInfoDlg::RI_FILE_NAME: return CCommon::LoadText(IDS_FILE_NAME); case CPluginInfoDlg::RI_FILE_PATH: return CCommon::LoadText(IDS_FILE_PATH); case CPluginInfoDlg::RI_ITEM_NUM: return CCommon::LoadText(IDS_ITEM_NUM); case CPluginInfoDlg::RI_ITEM_NAMES: return CCommon::LoadText(IDS_ITEM_NAMES); case CPluginInfoDlg::RI_ITEM_ID: return CCommon::LoadText(IDS_DISP_ITEM_ID); case CPluginInfoDlg::RI_AUTHOR: return CCommon::LoadText(IDS_AUTHOR); case CPluginInfoDlg::RI_COPYRIGHT: return CCommon::LoadText(IDS_COPYRIGHT); case CPluginInfoDlg::RI_URL: return CCommon::LoadText(IDS_URL); case CPluginInfoDlg::RI_VERSION: return CCommon::LoadText(IDS_VERSION); case CPluginInfoDlg::RI_API_VERSION: return CCommon::LoadText(IDS_PLUGIN_API_VERSION); default: break; } return CString(); } ================================================ FILE: TrafficMonitor/PluginInfoDlg.h ================================================ #pragma once #include "BaseDialog.h" #include "ListCtrlEx.h" // CPluginInfoDlg 对话框 class CPluginInfoDlg : public CBaseDialog { DECLARE_DYNAMIC(CPluginInfoDlg) public: CPluginInfoDlg(int plugin_index, CWnd* pParent = nullptr); // 标准构造函数 virtual ~CPluginInfoDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_NETWORK_INFO_DIALOG }; #endif private: int m_cur_index; //初始显示的插件索引 CListCtrlEx m_info_list; CMenu m_menu; CString m_selected_string; //列表中的列 enum RowIndex { RI_FILE_NAME, RI_FILE_PATH, RI_NAME, RI_DESCRIPTION, RI_ITEM_NUM, RI_ITEM_NAMES, RI_ITEM_ID, RI_AUTHOR, RI_COPYRIGHT, RI_URL, RI_VERSION, RI_API_VERSION, RI_MAX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 void ShowInfo(); static CString GetRowName(int row_index); //获取行的名称 // 通过 CBaseDialog 继承 virtual CString GetDialogName() const override; DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnCopyText(); afx_msg void OnNMRClickInfoList1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnBnClickedPreviousButton(); afx_msg void OnBnClickedNextButton(); virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); }; ================================================ FILE: TrafficMonitor/PluginManager.cpp ================================================ #include "stdafx.h" #include #include #include #include #include #include #include #include "DrawTextManager.h" #include "PluginManager.h" #include "Common.h" #include "TrafficMonitor.h" #define PLUGIN_UNSUPPORT_VERSION 0 //不被支持的插件版本 CPluginManager::CPluginManager() { } CPluginManager::~CPluginManager() { //卸载插件 for (const auto& m : m_modules) FreeLibrary(m.plugin_module); } static wstring WcharArrayToWString(const wchar_t* str) { if (str == nullptr) return wstring(); else return wstring(str); } void CPluginManager::LoadPlugins() { //从plugins目录下加载插件 wstring plugin_dir; plugin_dir = CCommon::GetModuleDir() + L"plugins"; vector plugin_files; CCommon::GetFiles((plugin_dir + L"\\*.dll").c_str(), plugin_files); //获取Plugins目录下所有的dll文件的文件名 for (const auto& file : plugin_files) { //插件信息 m_modules.push_back(PluginInfo()); PluginInfo& plugin_info{ m_modules.back() }; //插件dll的路径 plugin_info.file_path = plugin_dir + file; //插件文件名 std::wstring file_name{ file }; if (!file_name.empty() && (file_name[0] == L'\\' || file_name[0] == L'/')) file_name = file_name.substr(1); //如果插件被禁用,则不加载插件 if (theApp.m_cfg_data.plugin_disabled.Contains(file_name)) { plugin_info.state = PluginState::PS_DISABLE; continue; } //载入dll plugin_info.plugin_module = LoadLibrary(plugin_info.file_path.c_str()); if (plugin_info.plugin_module == NULL) { plugin_info.state = PluginState::PS_MUDULE_LOAD_FAILED; plugin_info.error_code = GetLastError(); continue; } // hook插件导入的User32.dll下的DrawText系列函数 ReplacePluginDrawTextFunction(plugin_info.plugin_module); //获取函数的入口地址 pfTMPluginGetInstance TMPluginGetInstance = (pfTMPluginGetInstance)::GetProcAddress(plugin_info.plugin_module, "TMPluginGetInstance"); if (TMPluginGetInstance == NULL) { plugin_info.state = PluginState::PS_FUNCTION_GET_FAILED; plugin_info.error_code = GetLastError(); continue; } //创建插件对象 plugin_info.plugin = TMPluginGetInstance(); if (plugin_info.plugin == nullptr) continue; //检查插件版本 int version = plugin_info.plugin->GetAPIVersion(); if (version <= PLUGIN_UNSUPPORT_VERSION) { plugin_info.state = PluginState::PS_VERSION_NOT_SUPPORT; continue; } //向插件传递配置文件的路径 std::wstring config_dir = theApp.m_config_dir; config_dir += L"plugins\\"; if (version >= 2) { CreateDirectory(config_dir.c_str(), NULL); //如果plugins不存在,则创建它 plugin_info.plugin->OnExtenedInfo(ITMPlugin::EI_CONFIG_DIR, config_dir.c_str()); } //调用初始化函数 if (version >= 7) { plugin_info.plugin->OnInitialize(&theApp); } //获取插件信息 for (int i{}; i < ITMPlugin::TMI_MAX; i++) { ITMPlugin::PluginInfoIndex index{ static_cast(i) }; plugin_info.properties[index] = WcharArrayToWString(plugin_info.plugin->GetInfo(index)); } //获取插件显示项目 int index = 0; while (true) { IPluginItem* item = plugin_info.plugin->GetItem(index); if (item == nullptr) break; plugin_info.plugin_items.push_back(item); m_plugins.push_back(item); m_plguin_item_map[item] = plugin_info.plugin; index++; } } // hook MFC导入的User32.dll下的DrawText系列函数 ReplaceMfcDrawTextFunction(); //初始化所有任务栏显示项目 for (const auto& display_item : AllDisplayItems) { m_all_display_items_with_plugins.insert(display_item); } for (const auto& display_item : m_plugins) { m_all_display_items_with_plugins.insert(display_item); } } const std::vector& CPluginManager::GetPluginItems() const { return m_plugins; } const std::vector& CPluginManager::GetPlugins() const { return m_modules; } IPluginItem* CPluginManager::GetItemById(const std::wstring& item_id) { for (const auto& item : m_plugins) { if (item->GetItemId() == item_id) return item; } return nullptr; } IPluginItem* CPluginManager::GetItemByIndex(int index) { if (index >= 0 && index < static_cast(m_plugins.size())) return m_plugins[index]; return nullptr; } int CPluginManager::GetItemIndex(IPluginItem* item) const { for (auto iter = m_plugins.begin(); iter != m_plugins.end(); ++iter) { if (*iter == item) return iter - m_plugins.begin(); } return -1; } ITMPlugin* CPluginManager::GetPluginByItem(IPluginItem* pItem) { if (pItem == nullptr) return nullptr; return m_plguin_item_map[pItem]; } int CPluginManager::GetPluginIndex(ITMPlugin* plugin) const { for (auto iter = m_modules.begin(); iter != m_modules.end(); ++iter) { if (iter->plugin == plugin) return iter - m_modules.begin(); } return -1; } void CPluginManager::EnumPlugin(std::function func) const { for (const auto& item : m_modules) { if (item.plugin != nullptr) { func(item.plugin); } } } void CPluginManager::EnumPluginItem(std::function func) const { for (const auto& item : m_plugins) { if (item != nullptr) { func(item); } } } const std::set& CPluginManager::AllDisplayItemsWithPlugins() { return m_all_display_items_with_plugins; } int CPluginManager::GetItemWidth(IPluginItem* pItem, CDC* pDC) { int width = 0; ITMPlugin* plugin = GetPluginByItem(pItem); if (plugin != nullptr && plugin->GetAPIVersion() >= 3) { width = pItem->GetItemWidthEx(pDC->GetSafeHdc()); //优先使用GetItemWidthEx接口获取宽度 } if (width == 0) { width = theApp.DPI(pItem->GetItemWidth()); } return width; } std::wstring CPluginManager::PluginInfo::Property(ITMPlugin::PluginInfoIndex index) const { auto iter = properties.find(index); if (iter != properties.end()) return iter->second; return wstring(); } template void inline CopyData(void* from, T* to) { ::memcpy(to, from, sizeof(T)); } auto inline GetCLocale() noexcept -> const std::locale& { const static std::locale restlt{"C"}; return restlt; }; /** * @brief 将输入的dll名称转大写 * * @param dll_name 输入的dll名称 * @param expected_length 期望的dll名称长度,不匹配则无操作 * @return std::string 大写的dll名称 */ std::string UpperDllName(const char* dll_name, std::size_t expected_length) { std::string result{}; result.resize(expected_length); const char* dll_name_last = &dll_name[expected_length]; std::transform(dll_name, dll_name_last, result.begin(), [](char c) -> char { return std::toupper(c, GetCLocale()); }); return result; } std::string UpperDllName(const char* dll_name) { std::string result{}; for (std::size_t i = 0; i < 256; ++i) { auto current_char = dll_name[i]; if (current_char == '\0') { result += '\0'; break; } result += std::toupper(current_char, GetCLocale()); } return result; } class DllName { public: class FunctionNameImpl { using Handler = std::function; friend DllName; public: /** * @brief Construct a new Function Name Impl object * * @param function_name 函数名称 * @param on_function_find_handler 当查找到函数时执行的方法, 类型为std::function */ FunctionNameImpl(const std::string& function_name, Handler on_function_find_handler) : m_function_name{function_name}, m_on_function_find_handler{on_function_find_handler}, m_p_function{} { } ~FunctionNameImpl() = default; void OnFunctionFind(void** pp_found_fuction, void* p_args) const { m_on_function_find_handler(pp_found_fuction, p_args); } auto NameString() const noexcept -> const std::string& { return m_function_name; } private: void* m_p_function; Handler m_on_function_find_handler; std::string m_function_name; }; using FunctionName = FunctionNameImpl; private: std::unordered_map m_function_name_map{}; std::string m_dll_name; public: DllName(const char* dll_name, std::size_t expected_length, std::initializer_list function_names_in_dll) : m_dll_name{UpperDllName(dll_name, expected_length)} { auto h_dll = ::LoadLibraryA(dll_name); if (h_dll) { for (auto function_name : function_names_in_dll) { auto* function_pointer = reinterpret_cast(::GetProcAddress(h_dll, function_name.NameString().c_str())); auto exist_it = m_function_name_map.find(function_pointer); if (exist_it == m_function_name_map.end()) { m_function_name_map.emplace(std::move(std::make_pair(function_pointer, function_name))); } } ::FreeLibrary(h_dll); } } ~DllName() = default; auto NameString() const noexcept -> const std::string& { return m_dll_name; } auto MatchFunctionPointer(void* found_pointer) const noexcept -> const FunctionName* { auto it = m_function_name_map.find(found_pointer); if (it != m_function_name_map.end()) { return &it->second; } return nullptr; } }; using FunctionName = DllName::FunctionNameImpl; class DllMap { private: std::unordered_map m_dll_name_map; public: DllMap(std::initializer_list dll_names) { for (auto&& dll_name : dll_names) { auto exist_dll_name = m_dll_name_map.find(dll_name.NameString()); if (exist_dll_name == m_dll_name_map.end()) { m_dll_name_map.emplace(std::make_pair(dll_name.NameString(), dll_name)); } } } ~DllMap() = default; auto MatchDllName(const char* any_case_string) const -> const DllName* { std::string capital_dll_name = UpperDllName(any_case_string); auto it = m_dll_name_map.find(capital_dll_name); if (it != m_dll_name_map.end()) { return &it->second; } return nullptr; } }; #define ARR_ARRLENGTH(c_array) c_array, GetArrayLength(c_array) void CPluginManager::ReplacePluginDrawTextFunction(HMODULE plgin_dll_module) noexcept { auto* p_plugin_dll_module = reinterpret_cast(plgin_dll_module); auto p_dos_header = reinterpret_cast(plgin_dll_module); auto p_image_nt_headers = reinterpret_cast( p_plugin_dll_module + p_dos_header->e_lfanew); auto p_optional_header = reinterpret_cast( &p_image_nt_headers->OptionalHeader); auto import_table_offset = p_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; auto p_current_import_desc = reinterpret_cast( p_plugin_dll_module + import_table_offset); //不区分大小写,需要输入的dll中导入表里User32.dll的4个函数 static const char user32_dll_name[] = "User32.dll"; static const DllName dll_names{ARR_ARRLENGTH(user32_dll_name), {{"DrawTextA", User32DrawTextManager::A::GetReplaceOperation()}, {"DrawTextW", User32DrawTextManager::W::GetReplaceOperation()}, {"DrawTextExA", User32DrawTextManager::ExA::GetReplaceOperation()}, {"DrawTextExW", User32DrawTextManager::ExW::GetReplaceOperation()}}}; static const DllMap dll_map{{dll_names}}; while (p_current_import_desc->FirstThunk != NULL) { const auto* p_import_dll_name = reinterpret_cast( p_plugin_dll_module + p_current_import_desc->Name); auto* p_matched_dll_name = dll_map.MatchDllName(p_import_dll_name); if (p_matched_dll_name != nullptr) { //搜索IAT表 auto p_current_chunk = reinterpret_cast( p_plugin_dll_module + p_current_import_desc->FirstThunk); while (p_current_chunk->u1.AddressOfData != NULL) { static_assert(sizeof(p_current_chunk->u1.Function) == sizeof(void*), "Length not match!"); auto p_target_pointer = reinterpret_cast(&p_current_chunk->u1.Function); auto p_matched_function = p_matched_dll_name->MatchFunctionPointer(*p_target_pointer); if (p_matched_function != nullptr) { p_matched_function->OnFunctionFind(p_target_pointer, nullptr); } ++p_current_chunk; } } ++p_current_import_desc; } } void CPluginManager::ReplaceMfcDrawTextFunction() noexcept { HMODULE mfc_module = ::LoadLibraryA("mfc140u.dll"); if (mfc_module != NULL) { ReplacePluginDrawTextFunction(mfc_module); ::FreeLibrary(mfc_module); } } ================================================ FILE: TrafficMonitor/PluginManager.h ================================================ #pragma once #include "PluginInterface.h" #include #include "TaskbarItemOrderHelper.h" #include typedef ITMPlugin* (*pfTMPluginGetInstance)(); //用于加载和管理插件 class CPluginManager { public: //插件的状态 enum class PluginState { PS_SUCCEED, //载入成功 PS_MUDULE_LOAD_FAILED, //dll加载失败 PS_FUNCTION_GET_FAILED, //插件函数获取失败 PS_VERSION_NOT_SUPPORT, //插件版本不被支持 PS_DISABLE //已禁用 }; //插件信息 struct PluginInfo { wstring file_path; //文件路径 HMODULE plugin_module{}; //dll module ITMPlugin* plugin{}; //插件对象 std::vector plugin_items; //插件提供的所有显示项目 PluginState state{}; //插件的状态 DWORD error_code{}; //错误代码(GetLastError的返回值) std::map properties; //插件属性 std::wstring Property(ITMPlugin::PluginInfoIndex) const; }; CPluginManager(); ~CPluginManager(); void LoadPlugins(); const std::vector& GetPluginItems() const; const std::vector& GetPlugins() const; IPluginItem* GetItemById(const std::wstring& item_id); IPluginItem* GetItemByIndex(int index); int GetItemIndex(IPluginItem* item) const; ITMPlugin* GetPluginByItem(IPluginItem* pItem); int GetPluginIndex(ITMPlugin* plugin) const; //遍历所有插件 //func: 参数为遍历到的ITMPlugin对象 void EnumPlugin(std::function func) const; //遍历所有插件项目 //func: 参数为遍历到的IPluginItem对象 void EnumPluginItem(std::function func) const; const std::set& AllDisplayItemsWithPlugins(); int GetItemWidth(IPluginItem* pItem, CDC* pDC); private: static void ReplacePluginDrawTextFunction(HMODULE plgin_dll_module) noexcept; static void ReplaceMfcDrawTextFunction() noexcept; private: std::vector m_plugins; std::vector m_modules; std::set m_all_display_items_with_plugins; //包含插件在内的所有任务栏显示项目 std::map m_plguin_item_map; //用于根据插件项目查找对应插件的map }; ================================================ FILE: TrafficMonitor/PluginManagerDlg.cpp ================================================ // PluginManagerDlg.cpp: 实现文件 // #include "stdafx.h" #include "TrafficMonitor.h" #include "PluginManagerDlg.h" #include "FilePathHelper.h" #include "PluginInfoDlg.h" #include "WIC.h" // CPluginManagerDlg 对话框 IMPLEMENT_DYNAMIC(CPluginManagerDlg, CBaseDialog) CPluginManagerDlg::CPluginManagerDlg(CWnd* pParent /*=nullptr*/) : CBaseDialog(IDD_PLUGIN_MANAGER_DIALOG, pParent) { } CPluginManagerDlg::~CPluginManagerDlg() { } void CPluginManagerDlg::DoDataExchange(CDataExchange* pDX) { CBaseDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_list_ctrl); DDX_Control(pDX, IDC_PLUGIN_DOWNLOAD_STATIC, m_plugin_download_lnk); DDX_Control(pDX, IDC_PLUGIN_DEV_GUID_STATIC, m_plugin_dev_guide_lnk); DDX_Control(pDX, IDC_OPEN_PLUGIN_DIR_STATIC, m_open_plugin_dir_lnk); } void CPluginManagerDlg::EnableControl() { bool enable{ IsSelectedPluginEnable() }; EnableDlgCtrl(IDC_OPTINS_BUTTON, enable); EnableDlgCtrl(IDC_PLUGIN_INFO_BUTTON, enable); } bool CPluginManagerDlg::IsSelectedValid() { return m_item_selected >= 0 && m_item_selected < static_cast(theApp.m_plugins.GetPlugins().size()); } bool CPluginManagerDlg::IsSelectedPluginEnable() { CPluginManager::PluginInfo plugin_info{}; bool plugin_loaded{ false }; if (IsSelectedValid()) { plugin_info = theApp.m_plugins.GetPlugins()[m_item_selected]; plugin_loaded = (plugin_info.state == CPluginManager::PluginState::PS_SUCCEED); } return plugin_loaded; } CString CPluginManagerDlg::GetDialogName() const { return _T("PluginManagerDlg"); } bool CPluginManagerDlg::InitializeControls() { RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_PLUGIN_INFO_BUTTON, CtrlTextInfo::W32 }, { CtrlTextInfo::L3, IDC_OPTINS_BUTTON, CtrlTextInfo::W32 } }); RepositionTextBasedControls({ { CtrlTextInfo::L4, IDC_PLUGIN_DOWNLOAD_STATIC }, { CtrlTextInfo::L3, IDC_PLUGIN_DEV_GUID_STATIC }, { CtrlTextInfo::L2, IDC_OPEN_PLUGIN_DIR_STATIC } }); return true; } BEGIN_MESSAGE_MAP(CPluginManagerDlg, CBaseDialog) ON_NOTIFY(NM_RCLICK, IDC_LIST1, &CPluginManagerDlg::OnNMRClickList1) ON_NOTIFY(NM_CLICK, IDC_LIST1, &CPluginManagerDlg::OnNMClickList1) ON_BN_CLICKED(IDC_OPTINS_BUTTON, &CPluginManagerDlg::OnBnClickedOptinsButton) ON_BN_CLICKED(IDC_PLUGIN_INFO_BUTTON, &CPluginManagerDlg::OnBnClickedPluginInfoButton) ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &CPluginManagerDlg::OnNMDblclkList1) ON_WM_INITMENU() ON_COMMAND(ID_PLUGIN_DETAIL, &CPluginManagerDlg::OnPluginDetail) ON_COMMAND(ID_PLUGIN_OPTIONS, &CPluginManagerDlg::OnPluginOptions) ON_COMMAND(ID_PLUGIN_DISABLE, &CPluginManagerDlg::OnPluginDisable) ON_MESSAGE(WM_LINK_CLICKED, &CPluginManagerDlg::OnLinkClicked) END_MESSAGE_MAP() // CPluginManagerDlg 消息处理程序 BOOL CPluginManagerDlg::OnInitDialog() { CBaseDialog::OnInitDialog(); // TODO: 在此添加额外的初始化 SetIcon(theApp.GetMenuIcon(IDI_PLUGINS), FALSE); //初始化列表控件 CRect rect; m_list_ctrl.GetClientRect(rect); m_list_ctrl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); int width0, width1, width2, width3; width0 = width1 = rect.Width() * 3 / 10; width3 = rect.Width() * 2 / 10; width2 = rect.Width() - width0 - width1 - width3 - theApp.DPI(20) - 1; m_list_ctrl.InsertColumn(COL_FILE_NAME, CCommon::LoadText(IDS_FILE_NAME), LVCFMT_LEFT, width0); m_list_ctrl.InsertColumn(COL_NAME, CCommon::LoadText(IDS_PLUGIN_NAME), LVCFMT_LEFT, width1); m_list_ctrl.InsertColumn(COL_VERSION, CCommon::LoadText(IDS_VERSION), LVCFMT_LEFT, width2); m_list_ctrl.InsertColumn(COL_STATUS, CCommon::LoadText(IDS_STATUS), LVCFMT_LEFT, width3); //添加图标 int item_num = theApp.m_plugins.GetPlugins().size(); m_plugin_icon_list.Create(theApp.DPI(16), theApp.DPI(16), ILC_COLOR32, item_num, item_num); for (const auto& plugin : theApp.m_plugins.GetPlugins()) { HICON hIcon{}; if (plugin.plugin != nullptr && plugin.plugin->GetAPIVersion() >= 5) { hIcon = (HICON)plugin.plugin->GetPluginIcon(); } if (hIcon == nullptr) { if (plugin.state == CPluginManager::PluginState::PS_DISABLE) hIcon = theApp.GetMenuIcon(IDI_PLUGIN_DISABLED); else hIcon = theApp.GetMenuIcon(IDI_PLUGINS); } m_plugin_icon_list.Add(hIcon); } m_list_ctrl.SetImageList(&m_plugin_icon_list, LVSIL_SMALL); //向列表中插入行 for (const auto& plugin : theApp.m_plugins.GetPlugins()) { std::wstring file_name = CFilePathHelper(plugin.file_path).GetFileName(); CString error_info; error_info.Format(_T("%u %s"), plugin.error_code, CCommon::GetErrorMessage(plugin.error_code).GetString()); CString status; switch (plugin.state) { case CPluginManager::PluginState::PS_SUCCEED: status = CCommon::LoadText(IDS_PLUGIN_LOAD_SUCCEED); break; case CPluginManager::PluginState::PS_MUDULE_LOAD_FAILED: status = CCommon::LoadTextFormat(IDS_PLUGIN_MODULE_LOAD_FAILED, { error_info }); break; case CPluginManager::PluginState::PS_FUNCTION_GET_FAILED: status = CCommon::LoadTextFormat(IDS_PLUGIN_FUNCTION_GET_FAILED, { error_info }); break; case CPluginManager::PluginState::PS_DISABLE: status = CCommon::LoadText(IDS_DISABLED); break; case CPluginManager::PluginState::PS_VERSION_NOT_SUPPORT: status = CCommon::LoadText(IDS_PLUGIN_VERSION_NOT_SUPPORT); break; default: break; } int index = m_list_ctrl.GetItemCount(); m_list_ctrl.InsertItem(index, file_name.c_str(), m_list_ctrl.GetItemCount()); m_list_ctrl.SetItemText(index, COL_NAME, plugin.Property(ITMPlugin::TMI_NAME).c_str()); if (plugin.state == CPluginManager::PluginState::PS_SUCCEED) { //如果插件有更新,在版本号后添加更新信息 PluginVersion cur_version(plugin.Property(ITMPlugin::TMI_VERSION)); PluginVersion latest_version(theApp.m_plugin_update.GetPluginLatestVersions(file_name)); if (cur_version < latest_version) { std::wstring update_info = theApp.m_str_table.LoadTextFormat(IDS_PLUGIN_NEW_VERSION_INFO, { latest_version.GetVersionWString() }); std::wstring version_text = plugin.Property(ITMPlugin::TMI_VERSION); version_text += L" ("; version_text += update_info; version_text += L')'; m_list_ctrl.SetItemText(index, COL_VERSION, version_text.c_str()); } else { m_list_ctrl.SetItemText(index, COL_VERSION, plugin.Property(ITMPlugin::TMI_VERSION).c_str()); } } m_list_ctrl.SetItemText(index, COL_STATUS, status); } if (theApp.m_general_data.update_source == 1) //更新源为Gitee,跳转到Gitee的链接 m_plugin_download_lnk.SetURL(L"https://gitee.com/zhongyang219/TrafficMonitorPlugins/blob/main/download/plugin_download.md"); else //更新源为Github m_plugin_download_lnk.SetURL(L"https://github.com/zhongyang219/TrafficMonitorPlugins/blob/main/download/plugin_download.md"); m_plugin_dev_guide_lnk.SetURL(L"https://github.com/zhongyang219/TrafficMonitor/wiki/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97"); m_open_plugin_dir_lnk.SetLinkIsURL(false); EnableControl(); CCommon::LoadMenuResource(m_menu, IDR_PLUGIN_MANAGER_MENU); //装载右键菜单 //设置菜单图标 CMenuIcon::AddIconToMenuItem(m_menu.GetSubMenu(0)->GetSafeHmenu(), ID_PLUGIN_DETAIL, FALSE, theApp.GetMenuIcon(IDI_INFO)); CMenuIcon::AddIconToMenuItem(m_menu.GetSubMenu(0)->GetSafeHmenu(), ID_PLUGIN_OPTIONS, FALSE, theApp.GetMenuIcon(IDI_SETTINGS)); //设置按钮图标 SetButtonIcon(IDC_PLUGIN_INFO_BUTTON, theApp.GetMenuIcon(IDI_INFO)); SetButtonIcon(IDC_OPTINS_BUTTON, theApp.GetMenuIcon(IDI_SETTINGS)); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CPluginManagerDlg::OnNMRClickList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 m_item_selected = pNMItemActivate->iItem; EnableControl(); //弹出右键菜单 CMenu* pContextMenu = m_menu.GetSubMenu(0); //获取第一个弹出菜单 pContextMenu->SetDefaultItem(ID_PLUGIN_DETAIL); //更新插件子菜单 if (m_item_selected >= 0 && m_item_selected < static_cast(theApp.m_plugins.GetPlugins().size())) { auto plugin_info = theApp.m_plugins.GetPlugins()[m_item_selected]; theApp.UpdatePluginMenu(pContextMenu, plugin_info.plugin, 3); } CPoint point1; //定义一个用于确定光标位置的位置 GetCursorPos(&point1); //获取当前光标的位置,以便使得菜单可以跟随光标 pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point1.x, point1.y, this); //在指定位置显示弹出菜单 *pResult = 0; } void CPluginManagerDlg::OnNMClickList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 m_item_selected = pNMItemActivate->iItem; EnableControl(); *pResult = 0; } void CPluginManagerDlg::OnBnClickedOptinsButton() { // TODO: 在此添加控件通知处理程序代码 if (m_item_selected >= 0 && m_item_selected < static_cast(theApp.m_plugins.GetPlugins().size())) { auto plugin_info = theApp.m_plugins.GetPlugins()[m_item_selected]; if (plugin_info.plugin != nullptr) { ITMPlugin::OptionReturn rtn = plugin_info.plugin->ShowOptionsDialog(m_hWnd); if (rtn == ITMPlugin::OR_OPTION_NOT_PROVIDED) MessageBox(CCommon::LoadText(IDS_PLUGIN_NO_OPTIONS_INFO), nullptr, MB_ICONINFORMATION | MB_OK); //else if (rtn == ITMPlugin::OR_OPTION_CHANGED) // theApp.m_pMainWnd->SendMessage(WM_REOPEN_TASKBAR_WND); } } } void CPluginManagerDlg::OnBnClickedPluginInfoButton() { // TODO: 在此添加控件通知处理程序代码 if (IsSelectedPluginEnable()) { CPluginInfoDlg dlg(m_item_selected); dlg.DoModal(); } } void CPluginManagerDlg::OnNMDblclkList1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 m_item_selected = pNMItemActivate->iItem; OnBnClickedPluginInfoButton(); *pResult = 0; } void CPluginManagerDlg::OnInitMenu(CMenu* pMenu) { CBaseDialog::OnInitMenu(pMenu); // TODO: 在此处添加消息处理程序代码 bool enable{ IsSelectedPluginEnable() }; pMenu->EnableMenuItem(ID_PLUGIN_DETAIL, MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED)); pMenu->EnableMenuItem(ID_PLUGIN_OPTIONS, MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED)); pMenu->EnableMenuItem(ID_PLUGIN_DISABLE, MF_BYCOMMAND | (IsSelectedValid() ? MF_ENABLED : MF_GRAYED)); bool disabled{}; CPluginManager::PluginInfo plugin_info; if (m_item_selected >= 0 && m_item_selected < static_cast(theApp.m_plugins.GetPlugins().size())) { plugin_info = theApp.m_plugins.GetPlugins()[m_item_selected]; std::wstring file_name = CFilePathHelper(plugin_info.file_path).GetFileName(); disabled = theApp.m_cfg_data.plugin_disabled.Contains(file_name); } pMenu->CheckMenuItem(ID_PLUGIN_DISABLE, MF_BYCOMMAND | (disabled ? MF_CHECKED : MF_UNCHECKED)); } void CPluginManagerDlg::OnPluginDetail() { OnBnClickedPluginInfoButton(); } void CPluginManagerDlg::OnPluginOptions() { OnBnClickedOptinsButton(); } void CPluginManagerDlg::OnPluginDisable() { if (m_item_selected >= 0 && m_item_selected < static_cast(theApp.m_plugins.GetPlugins().size())) { CPluginManager::PluginInfo plugin_info = theApp.m_plugins.GetPlugins()[m_item_selected]; std::wstring file_name = CFilePathHelper(plugin_info.file_path).GetFileName(); bool disabled = theApp.m_cfg_data.plugin_disabled.Contains(file_name); theApp.m_cfg_data.plugin_disabled.SetStrContained(file_name, !disabled); MessageBox(CCommon::LoadText(IDS_RESTART_TO_APPLY_CHANGE_INFO), nullptr, MB_OK | MB_ICONINFORMATION); } } afx_msg LRESULT CPluginManagerDlg::OnLinkClicked(WPARAM wParam, LPARAM lParam) { CWnd* pCtrl = (CWnd*)wParam; //点击了“打开插件目录” if (pCtrl == &m_open_plugin_dir_lnk) { wstring plugin_dir = CCommon::GetModuleDir() + L"plugins"; CreateDirectory(plugin_dir.c_str(), NULL); //如果plugins不存在,则创建它 ShellExecute(NULL, _T("open"), _T("explorer"), plugin_dir.c_str(), NULL, SW_SHOWNORMAL); } return 0; } BOOL CPluginManagerDlg::OnCommand(WPARAM wParam, LPARAM lParam) { UINT uMsg = LOWORD(wParam); //选择了插件命令 if (uMsg >= ID_PLUGIN_COMMAND_START && uMsg <= ID_PLUGIN_COMMAND_MAX) { int index = uMsg - ID_PLUGIN_COMMAND_START; if (m_item_selected >= 0 && m_item_selected < static_cast(theApp.m_plugins.GetPlugins().size())) { ITMPlugin* plugin = theApp.m_plugins.GetPlugins()[m_item_selected].plugin; if (plugin != nullptr && plugin->GetAPIVersion() >= 5) { plugin->OnPluginCommand(index, (void*)GetSafeHwnd(), nullptr); } } } return CBaseDialog::OnCommand(wParam, lParam); } ================================================ FILE: TrafficMonitor/PluginManagerDlg.h ================================================ #pragma once #include "BaseDialog.h" #include "LinkStatic.h" #include "ListCtrlEx.h" // CPluginManagerDlg 对话框 class CPluginManagerDlg : public CBaseDialog { DECLARE_DYNAMIC(CPluginManagerDlg) public: CPluginManagerDlg(CWnd* pParent = nullptr); // 标准构造函数 virtual ~CPluginManagerDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_PLUGIN_MANAGER_DIALOG }; #endif private: CListCtrlEx m_list_ctrl; int m_item_selected{ -1 }; CMenu m_menu; CLinkStatic m_plugin_download_lnk; CLinkStatic m_plugin_dev_guide_lnk; CLinkStatic m_open_plugin_dir_lnk; CImageList m_plugin_icon_list; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 void EnableControl(); bool IsSelectedValid(); bool IsSelectedPluginEnable(); //选中插件是否可用 enum Column { COL_FILE_NAME, COL_NAME, COL_VERSION, COL_STATUS, COL_MAX }; DECLARE_MESSAGE_MAP() // 通过 CBaseDialog 继承 virtual CString GetDialogName() const override; virtual bool InitializeControls() override; public: virtual BOOL OnInitDialog(); afx_msg void OnNMRClickList1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnNMClickList1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnBnClickedOptinsButton(); afx_msg void OnBnClickedPluginInfoButton(); afx_msg void OnNMDblclkList1(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnInitMenu(CMenu* pMenu); afx_msg void OnPluginDetail(); afx_msg void OnPluginOptions(); afx_msg void OnPluginDisable(); protected: afx_msg LRESULT OnLinkClicked(WPARAM wParam, LPARAM lParam); virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); }; ================================================ FILE: TrafficMonitor/PluginUpdateHelper.cpp ================================================ #include "stdafx.h" #include "PluginUpdateHelper.h" #include "Common.h" #include "TrafficMonitor.h" #include "TinyXml2Helper.h" static void VersionFromString(const std::string& version_str, std::vector& versions) { versions.clear(); //拆分字符串 std::vector vec_version; CCommon::StringSplit(version_str, '.', vec_version); //转换为整数保存 for (const auto& str : vec_version) { //去掉除数字以外的字符 std::string str_ver; for (auto ch : str) { if (ch >= '0' && ch <= '9') str_ver.push_back(ch); } versions.push_back(atoi(str_ver.c_str())); } } ///////////////////////////////////////////////////////////////////////////////////////////////////// PluginVersion::PluginVersion() { } PluginVersion::PluginVersion(const std::wstring& version) { m_version_str = CCommon::AsciiToStr(version); VersionFromString(m_version_str, m_version); } PluginVersion::PluginVersion(const std::string& version) { m_version_str = version; VersionFromString(m_version_str, m_version); } bool PluginVersion::operator<(const PluginVersion& another) const { for (size_t i{}; i < m_version.size() || i < another.m_version.size(); i++) { if (GetSubVersion(i) < another.GetSubVersion(i)) return true; } return false; } bool PluginVersion::operator==(const PluginVersion& another) const { for (size_t i{}; i < m_version.size() || i < another.m_version.size(); i++) { if (GetSubVersion(i) != another.GetSubVersion(i)) return false; } return true; } std::string PluginVersion::GetVersionString() const { return m_version_str; } std::wstring PluginVersion::GetVersionWString() const { return CCommon::AsciiToUnicode(m_version_str); } int PluginVersion::GetSubVersion(size_t index) const { if (index < m_version.size()) return m_version[index]; return 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////// bool CPluginUpdateHelper::CheckForUpdate() { m_latest_versions.clear(); std::wstring url; //更新源为Gitee if (theApp.m_general_data.update_source == 1) url = L"https://gitee.com/zhongyang219/TrafficMonitorPlugins/raw/main/plugins_version.xml"; //更新源为Github else url = L"https://raw.githubusercontent.com/zhongyang219/TrafficMonitorPlugins/main/plugins_version.xml"; //获取url内容 std::string version_info; if (CCommon::GetURL(url, version_info)) { //解析xml,获取版本信息 tinyxml2::XMLDocument doc; doc.Parse(version_info.c_str(), version_info.size()); //遍历插件版本 CTinyXml2Helper::IterateChildNode(doc.RootElement(), [&](tinyxml2::XMLElement* ele) { std::wstring file_name = CCommon::StrToUnicode(CTinyXml2Helper::ElementAttribute(ele, "file_name")); std::string plugin_version(CTinyXml2Helper::ElementAttribute(ele, "version")); PluginVersion version(plugin_version); m_latest_versions.emplace(file_name, version); }); return true; } return false; } const PluginVersion& CPluginUpdateHelper::GetPluginLatestVersions(const std::wstring& file_name) const { auto iter = m_latest_versions.find(file_name); if (iter != m_latest_versions.end()) { return iter->second; } static PluginVersion empty_version; return empty_version; } ================================================ FILE: TrafficMonitor/PluginUpdateHelper.h ================================================ #pragma once class PluginVersion { public: PluginVersion(); PluginVersion(const std::wstring& version); PluginVersion(const std::string& version); bool operator<(const PluginVersion& another) const; bool operator==(const PluginVersion& another) const; std::string GetVersionString() const; std::wstring GetVersionWString() const; protected: int GetSubVersion(size_t index) const; private: std::vector m_version; std::string m_version_str; }; class CPluginUpdateHelper { public: bool CheckForUpdate(); //获取一个插件的最新版本 const PluginVersion& GetPluginLatestVersions(const std::wstring& file_name) const; private: std::map m_latest_versions; //在线获取到的每个插件的最新版本信息 }; ================================================ FILE: TrafficMonitor/ReadMe.txt ================================================ ================================================================================ MICROSOFT 基础类库 : TrafficMonitor 项目概述 =============================================================================== 应用程序向导已为您创建了此 TrafficMonitor 应用程序。此应用程序不仅演示 Microsoft 基础类的基本使用方法,还可作为您编写应用程序的起点。 本文件概要介绍组成 TrafficMonitor 应用程序的每个文件的内容。 TrafficMonitor.vcxproj 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。 TrafficMonitor.vcxproj.filters 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。 TrafficMonitor.h 这是应用程序的主头文件。 其中包括其他项目特定的标头(包括 Resource.h),并声明 CTrafficMonitorApp 应用程序类。 TrafficMonitor.cpp 这是包含应用程序类 CTrafficMonitorApp 的主应用程序源文件。 TrafficMonitor.rc 这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。此文件可以直接在 Microsoft Visual C++ 中进行编辑。项目资源包含在 2052 中。 res\TrafficMonitor.ico 这是用作应用程序图标的图标文件。此图标包括在主资源文件 TrafficMonitor.rc 中。 res\TrafficMonitor.rc2 此文件包含不在 Microsoft Visual C++ 中进行编辑的资源。您应该将不可由资源编辑器编辑的所有资源放在此文件中。 ///////////////////////////////////////////////////////////////////////////// 应用程序向导创建一个对话框类: TrafficMonitorDlg.h、TrafficMonitorDlg.cpp - 对话框 这些文件包含 CTrafficMonitorDlg 类。此类定义应用程序的主对话框的行为。对话框模板包含在 TrafficMonitor.rc 中,该文件可以在 Microsoft Visual C++ 中编辑。 ///////////////////////////////////////////////////////////////////////////// 其他功能: ActiveX 控件 该应用程序包含对使用 ActiveX 控件的支持。 ///////////////////////////////////////////////////////////////////////////// 其他标准文件: StdAfx.h, StdAfx.cpp 这些文件用于生成名为 TrafficMonitor.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。 Resource.h 这是标准头文件,可用于定义新的资源 ID。Microsoft Visual C++ 将读取并更新此文件。 TrafficMonitor.manifest Windows XP 使用应用程序清单文件来描述特定版本的并行程序集的应用程序依赖项。加载程序使用这些信息来从程序集缓存中加载相应的程序集,并保护其不被应用程序访问。应用程序清单可能会包含在内,以作为与应用程序可执行文件安装在同一文件夹中的外部 .manifest 文件进行重新分发,它还可能以资源的形式包含在可执行文件中。 ///////////////////////////////////////////////////////////////////////////// 其他注释: 应用程序向导使用“TODO:”来指示应添加或自定义的源代码部分。 如果应用程序使用共享 DLL 中的 MFC,您将需要重新分发 MFC DLL。如果应用程序所使用的语言与操作系统的区域设置不同,则还需要重新分发相应的本地化资源 mfc110XXX.DLL。 有关上述话题的更多信息,请参见 MSDN 文档中有关重新分发 Visual C++ 应用程序的部分。 ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: TrafficMonitor/RenderAPISupport.h ================================================ #pragma once #include #include #include #include #define TRAFFICMONITOR_HAS_FUNCTION_CALL(function_name, class_name) \ ::is_function_##function_name##_exist::value #define TRAFFICMONITOR_DEFINE_HAS_FUNCTION_CALL(function_name) \ template \ class is_function_##function_name##_exist \ { \ using Existence = char; \ struct Inexistence \ { \ char unused[2]; \ }; \ \ template \ static Existence test(decltype(&C::function_name)); \ template \ static Inexistence test(...); \ \ public: \ constexpr static bool value = sizeof(test(0)) == sizeof(Existence); \ } TRAFFICMONITOR_DEFINE_HAS_FUNCTION_CALL(AddResource); TRAFFICMONITOR_DEFINE_HAS_FUNCTION_CALL(RemoveResource); TRAFFICMONITOR_DEFINE_HAS_FUNCTION_CALL(Recreate); template struct MutipleStorage : public Ts... { }; template using storage_t = typename T::Storage; template using resource_t = typename T::Resource; template struct get_first_template_argument; template